├── examples ├── compile.sh ├── README.md └── basic.c ├── .gitignore ├── test ├── fixtures │ ├── star_wars.json │ ├── star_wars_append.json │ └── star_wars_merged.json ├── CMakeLists.txt ├── test_parser.cpp ├── test_iterator.cpp ├── test_json_generator.cpp └── test_basic.cpp ├── README.md ├── pillowtalk.spec ├── MIT-LICENSE ├── src ├── CMakeLists.txt ├── pillowtalk_impl.h ├── pillowtalk.h ├── utlist.h ├── bsd_queue.h ├── pillowtalk_impl.c └── uthash.h ├── CMakeLists.txt └── configure /examples/compile.sh: -------------------------------------------------------------------------------- 1 | gcc -lpillowtalk -o basic basic.c 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | Makefile 3 | Testing 4 | examples/basic 5 | -------------------------------------------------------------------------------- /test/fixtures/star_wars.json: -------------------------------------------------------------------------------- 1 | { 2 | "Star Wars" : { 3 | "movies" : { 4 | "Star Wars Episode IV": { 5 | "characters": ["Luke Skywalker","Han Solo"] 6 | } 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | You can compile these examples by first installing the pillowtalk library, and 4 | then using compile.sh to do simple gcc commands. Make sure you fire up couchdb 5 | at localhost:5984 for things to actually work 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pillowtalk: CouchDB C API based on libcurl and yajl 2 | 3 | This library is a basic wrapper around libcurl and yajl that attempts to 4 | provide a more generic interface to couchdb, mainly communicating via url 5 | targets rather than explicit method calls. More to come... 6 | -------------------------------------------------------------------------------- /test/fixtures/star_wars_append.json: -------------------------------------------------------------------------------- 1 | { 2 | "Star Wars" : { 3 | "movies" : { 4 | "Star Wars Episode IV": { 5 | "characters" : ["Luke Skywalker","Obi Wan Kenobi","Han Solo"], 6 | "year" : 1977 7 | }, 8 | 9 | "Star Wars Episode V": { 10 | "characters" : ["Luke Skywalker","Darth Vader","Yoda"], 11 | "year" : 1980 12 | } 13 | }, 14 | "books" : ["Lots of them"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/star_wars_merged.json: -------------------------------------------------------------------------------- 1 | { 2 | "Star Wars": { 3 | "movies": { 4 | "Star Wars Episode IV": { 5 | "characters": [ 6 | "Luke Skywalker", 7 | "Obi Wan Kenobi", 8 | "Han Solo" 9 | ], 10 | "year": 1977 11 | }, 12 | "Star Wars Episode V": { 13 | "characters": [ 14 | "Luke Skywalker", 15 | "Darth Vader", 16 | "Yoda" 17 | ], 18 | "year": 1980 19 | } 20 | }, 21 | "books": [ 22 | "Lots of them" 23 | ] 24 | } 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package( Boost COMPONENTS) 2 | if (Boost_FOUND) 3 | include_directories(${Boost_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/../src/) 4 | link_libraries(${Boost_LIBARIES} pillowtalk) 5 | 6 | MESSAGE("-- Boost Include Dir:" ${Boost_INCLUDE_DIRS}) 7 | 8 | set(HDRS ${CMAKE_CURRENT_SOURCE_DIR}/../src/pillowtalk.h) 9 | 10 | add_executable(test_basic test_basic.cpp ${HDRS}) 11 | add_executable(test_json_generator test_json_generator.cpp ${HDRS}) 12 | add_executable(test_iterator test_iterator.cpp ${HDRS}) 13 | add_executable(test_parser test_parser.cpp ${HDRS}) 14 | 15 | enable_testing() 16 | 17 | add_test(basic test_basic ) 18 | add_test(json_generator test_json_generator ${CMAKE_CURRENT_SOURCE_DIR}) 19 | add_test(iterator test_iterator) 20 | add_test(parser test_parser ${CMAKE_CURRENT_SOURCE_DIR}) 21 | else (Boost_FOUND) 22 | message("Install Boost to do testing") 23 | endif (Boost_FOUND) 24 | -------------------------------------------------------------------------------- /pillowtalk.spec: -------------------------------------------------------------------------------- 1 | Name: pillowtalk 2 | Version: 0.3 3 | Summary: ANSI C library that talks to CouchDB using libcurl and yajl 4 | Release: 2 5 | Source: %{name}-%{version}.tgz 6 | 7 | License: MIT-LICENSE 8 | Group: Development/Tools 9 | URL: http://github.com/jubos/pillowtalk/ 10 | Packager: Guy Albertelli (guy@kosmix.com) 11 | BuildRoot: /var/tmp/%{name}-%{version}.root 12 | 13 | %description 14 | ANSI C library that talks to CouchDB using libcurl and yajl 15 | 16 | 17 | %prep 18 | %setup -n %{name}-%{version} 19 | 20 | %build 21 | pwd 22 | ./configure --prefix=$RPM_BUILD_ROOT 23 | cmake -DCMAKE_INSTALL_PREFIX=$RPM_BUILD_ROOT/usr 24 | make 25 | 26 | %install 27 | rm -rf $RPM_BUILD_ROOT 28 | mkdir -p $RPM_BUILD_ROOT%{_bindir} 29 | mkdir -p $RPM_BUILD_ROOT%{_docdir}/%{name}-%{version} 30 | make install 31 | #cp -R *.html *.png *.css LICENSE*.txt images jam $RPM_BUILD_ROOT%{_docdir}/%{name}-%{version} 32 | 33 | %files 34 | %defattr(-,root,root) 35 | %attr(644,root,root) /usr/lib/* 36 | %attr(644,root,root) /usr/include/* 37 | 38 | 39 | 40 | %clean 41 | rm -rf $RPM_BUILD_ROOT 42 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Curtis W Spencer (@jubos) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | SET( pillowtalk_SRCS pillowtalk_impl.c ) 2 | SET (pillowtalk_HDRS pillowtalk.h pillowtalk_impl.h) 3 | 4 | ADD_LIBRARY( pillowtalk SHARED ${pillowtalk_SRCS} ${pillowtalk_HDRS} ) 5 | 6 | #### setup shared library version number 7 | SET_TARGET_PROPERTIES(pillowtalk PROPERTIES 8 | SOVERSION ${PILLOWTALK_MAJOR} 9 | VERSION ${PILLOWTALK_MAJOR}.${PILLOWTALK_MINOR}.${PILLOWTALK_MICRO}) 10 | 11 | TARGET_LINK_LIBRARIES(pillowtalk ${YAJL_LIBRARY} ${CURL_LIBRARY}) 12 | 13 | # Output Paths 14 | SET (output_include ${CMAKE_CURRENT_BINARY_DIR}/../include) 15 | FILE(MAKE_DIRECTORY ${output_include}) 16 | 17 | # Copy Public .h's into the output directory so the tests can get it 18 | #SET (pillowtalk_PUB_HDRS pillowtalk.h) 19 | #FOREACH (header ${pillowtalk_PUB_HDRS}) 20 | # SET (header ${CMAKE_CURRENT_SOURCE_DIR}/${header}) 21 | 22 | # EXEC_PROGRAM(${CMAKE_COMMAND} ARGS -E copy_if_different ${header} ${output_include}) 23 | 24 | #ENDFOREACH (header ${pillowtalk_PUB_HDRS}) 25 | 26 | # Installation Step 27 | INSTALL( TARGETS pillowtalk LIBRARY DESTINATION lib) 28 | INSTALL( FILES pillowtalk.h DESTINATION include) 29 | -------------------------------------------------------------------------------- /test/test_parser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define BOOST_TEST_MAIN 7 | #include 8 | #include "pillowtalk.h" 9 | 10 | using namespace std; 11 | using namespace boost::unit_test; 12 | 13 | static string 14 | read_file(const string& filename) 15 | { 16 | string test_files_dir = string(boost::unit_test::framework::master_test_suite().argv[1]); 17 | stringstream content; 18 | string line; 19 | ifstream myfile((test_files_dir + "/" + filename).c_str()); 20 | if (myfile.is_open()) 21 | { 22 | while (! myfile.eof() ) 23 | { 24 | getline (myfile,line); 25 | content << line << endl; 26 | } 27 | myfile.close(); 28 | } else { 29 | cout << "Unable to open file" << endl; 30 | exit(-1); 31 | } 32 | return content.str(); 33 | } 34 | 35 | BOOST_AUTO_TEST_CASE( test_memory_leak ) 36 | { 37 | pt_node_t* root = pt_from_json("{\"test\":{hello:\"world\"}}"); 38 | BOOST_REQUIRE(pt_map_get(root,"test")); 39 | pt_free_node(root); 40 | } 41 | 42 | BOOST_AUTO_TEST_CASE( test_bad_json ) 43 | { 44 | pt_node_t* root = pt_from_json("{}}"); 45 | pt_free_node(root); 46 | } 47 | 48 | BOOST_AUTO_TEST_CASE( test_simple_array ) 49 | { 50 | pt_node_t* array = pt_from_json("[\"1\"]"); 51 | BOOST_REQUIRE_EQUAL(pt_array_len(array),1); 52 | pt_node_t* str = pt_array_get(array,0); 53 | BOOST_REQUIRE_EQUAL(pt_string_get(str),"1"); 54 | 55 | pt_free_node(array); 56 | } 57 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 2 | SET(CMAKE_VERBOSE_MAKEFILE off) 3 | PROJECT( pillowtalk ) 4 | 5 | SET(CMAKE_C_FLAGS "-Wall") 6 | SET(CMAKE_C_FLAGS_DEBUG "-DDEBUG -g") 7 | SET(CMAKE_C_FLAGS_RELEASE "-DNDEBUG -O2 -Wuninitialized") 8 | 9 | SET (PILLOWTALK_MAJOR 0) 10 | SET (PILLOWTALK_MINOR 3) 11 | SET (PILLOWTALK_MICRO 0) 12 | 13 | # Default to Release type 14 | IF (NOT CMAKE_BUILD_TYPE) 15 | SET(CMAKE_BUILD_TYPE "Release") 16 | ENDIF (NOT CMAKE_BUILD_TYPE) 17 | 18 | # Here we look for curl 19 | #FIND_PATH(CURL_INCLUDE_DIR curl.h /usr/local/include/curl /usr/include/curl ) 20 | #FIND_LIBRARY(CURL_LIBRARY NAMES curl PATHS /usr/local/lib /usr/lib NO_DEFAULT_PATH) 21 | 22 | FIND_PATH(CURL_INCLUDE_DIR curl/curl.h) 23 | FIND_LIBRARY(CURL_LIBRARY NAMES curl) 24 | 25 | MESSAGE("-- Curl Include Dir:" ${CURL_INCLUDE_DIR}) 26 | MESSAGE("-- Curl Library:" ${CURL_LIBRARY}) 27 | 28 | IF (CURL_INCLUDE_DIR AND CURL_LIBRARY) 29 | SET(CURL_FOUND TRUE) 30 | ELSE (CURL_INCLUDE_DIR AND CURL_LIBRARY) 31 | MESSAGE(FATAL_ERROR "Could not find CURL") 32 | ENDIF (CURL_INCLUDE_DIR AND CURL_LIBRARY) 33 | 34 | INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) 35 | 36 | # Here we look for yajl 37 | FIND_PATH(YAJL_INCLUDE_DIR yajl/yajl_parse.h) 38 | FIND_FILE(YAJL_VERSION yajl/yajl_version.h) 39 | FIND_LIBRARY(YAJL_LIBRARY NAMES yajl) 40 | 41 | MESSAGE("-- yajl Include Dir:" ${YAJL_INCLUDE_DIR}) 42 | MESSAGE("-- yajl Library:" ${YAJL_LIBRARY}) 43 | MESSAGE("-- yajl Version File:" ${YAJL_VERSION}) 44 | 45 | IF (YAJL_INCLUDE_DIR AND YAJL_LIBRARY) 46 | SET(YAJL_FOUND TRUE) 47 | ELSE (YAJL_INCLUDE_DIR AND YAJL_LIBRARY) 48 | MESSAGE(FATAL_ERROR "Could not find YAJL") 49 | ENDIF (YAJL_INCLUDE_DIR AND YAJL_LIBRARY) 50 | IF (YAJL_VERSION) 51 | ADD_DEFINITIONS(-DPT_HAVE_YAJL_VERSION) 52 | ENDIF (YAJL_VERSION) 53 | 54 | INCLUDE_DIRECTORIES(${YAJL_INCLUDE_DIR}) 55 | 56 | add_subdirectory(src) 57 | add_subdirectory(test) 58 | 59 | enable_testing() 60 | -------------------------------------------------------------------------------- /src/pillowtalk_impl.h: -------------------------------------------------------------------------------- 1 | #include "pillowtalk.h" 2 | #include "uthash.h" 3 | #include "utlist.h" 4 | #include "bsd_queue.h" 5 | 6 | /* Here we have "subclasses" of pt_node */ 7 | 8 | struct pt_map_t; 9 | 10 | typedef enum {PT_ARRAY_ITERATOR, PT_MAP_ITERATOR} pt_iterator_type; 11 | 12 | typedef struct { 13 | pt_node_t parent; 14 | char* key; 15 | pt_node_t* value; 16 | struct pt_map_t* map; 17 | UT_hash_handle hh; 18 | } pt_key_value_t; 19 | 20 | typedef struct { 21 | pt_node_t parent; 22 | pt_key_value_t* key_values; 23 | } pt_map_t; 24 | 25 | typedef struct pt_array_elem_t { 26 | pt_node_t* node; 27 | //struct pt_array_elem_t* next, *prev; 28 | TAILQ_ENTRY(pt_array_elem_t) entries; 29 | } pt_array_elem_t; 30 | 31 | typedef struct { 32 | pt_node_t parent; 33 | //pt_array_elem_t* head; 34 | TAILQ_HEAD(,pt_array_elem_t) head; 35 | //pt_node_t** array; 36 | unsigned int len; 37 | } pt_array_t; 38 | 39 | typedef struct { 40 | pt_node_t parent; 41 | int value; 42 | } pt_null_value_t; 43 | 44 | typedef struct { 45 | pt_node_t parent; 46 | int value; 47 | } pt_bool_value_t; 48 | 49 | typedef struct { 50 | pt_node_t parent; 51 | int value; 52 | } pt_int_value_t; 53 | 54 | typedef struct { 55 | pt_node_t parent; 56 | double value; 57 | } pt_double_value_t; 58 | 59 | typedef struct { 60 | pt_node_t parent; 61 | char* value; 62 | } pt_str_value_t; 63 | 64 | /* This is useful for a stack of containers so we can know where we are */ 65 | typedef struct pt_container_ctx_t { 66 | pt_node_t* container; 67 | pt_node_t* cur; 68 | struct pt_container_ctx_t *next;//, *prev; 69 | }pt_container_ctx_t; 70 | 71 | /* Implementation Structure of pt_response_t */ 72 | typedef struct { 73 | pt_node_t* root; 74 | pt_container_ctx_t* stack; 75 | } pt_parser_ctx_t; 76 | 77 | typedef struct { 78 | pt_iterator_type type; 79 | pt_array_elem_t* next_array_elem; 80 | pt_key_value_t* next_map_pair; 81 | } pt_iterator_impl_t; 82 | 83 | -------------------------------------------------------------------------------- /test/test_iterator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define BOOST_TEST_MAIN 6 | #include 7 | #include "pillowtalk.h" 8 | 9 | using namespace std; 10 | using namespace boost::unit_test; 11 | 12 | BOOST_AUTO_TEST_CASE( test_iterator ) 13 | { 14 | pt_node_t* array = pt_array_new(); 15 | pt_array_push_back(array,pt_string_new("1")); 16 | pt_array_push_back(array,pt_string_new("2")); 17 | pt_array_push_back(array,pt_string_new("3")); 18 | 19 | pt_iterator_t* iter = pt_iterator(array); 20 | int index = 0; 21 | while(pt_node_t* elem = pt_iterator_next(iter,NULL)) { 22 | switch(index) { 23 | case 0: 24 | BOOST_REQUIRE_EQUAL(pt_string_get(elem),"1"); 25 | break; 26 | case 1: 27 | BOOST_REQUIRE_EQUAL(pt_string_get(elem),"2"); 28 | break; 29 | case 2: 30 | BOOST_REQUIRE_EQUAL(pt_string_get(elem),"3"); 31 | break; 32 | } 33 | index++; 34 | } 35 | free(iter); 36 | pt_free_node(array); 37 | } 38 | 39 | BOOST_AUTO_TEST_CASE( test_blank_iterator) 40 | { 41 | pt_node_t* array = pt_array_new(); 42 | 43 | pt_iterator_t* iter = pt_iterator(array); 44 | pt_node_t* elem = pt_iterator_next(iter,NULL); 45 | BOOST_REQUIRE(!elem); 46 | free(iter); 47 | pt_free_node(array); 48 | } 49 | 50 | BOOST_AUTO_TEST_CASE( test_map_iterator) 51 | { 52 | pt_node_t* map = pt_map_new(); 53 | pt_map_set(map,"1",pt_integer_new(1)); 54 | pt_map_set(map,"2",pt_integer_new(2)); 55 | pt_map_set(map,"3",pt_integer_new(3)); 56 | 57 | pt_iterator_t* iter = pt_iterator(map); 58 | pt_node_t* elem; 59 | const char* key = NULL; 60 | int index = 0; 61 | while(pt_node_t* elem = pt_iterator_next(iter,&key)) { 62 | switch(index) { 63 | case 0: 64 | BOOST_REQUIRE_EQUAL(pt_integer_get(elem),1); 65 | BOOST_REQUIRE_EQUAL(key,"1"); 66 | break; 67 | case 1: 68 | BOOST_REQUIRE_EQUAL(pt_integer_get(elem),2); 69 | BOOST_REQUIRE_EQUAL(key,"2"); 70 | break; 71 | case 2: 72 | BOOST_REQUIRE_EQUAL(pt_integer_get(elem),3); 73 | BOOST_REQUIRE_EQUAL(key,"3"); 74 | break; 75 | } 76 | index++; 77 | } 78 | elem = pt_iterator_next(iter,NULL); 79 | BOOST_REQUIRE(!elem); 80 | free(iter); 81 | pt_free_node(map); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Copyright 2009, Curtis Spencer. 4 | # 5 | # This configure script is based on Lloyd Hilaiel's configure script in yajl 6 | # Copyright 2007-2009, Lloyd Hilaiel. 7 | # 8 | require 'fileutils' 9 | require 'optparse' 10 | 11 | options = {} 12 | 13 | OptionParser.new do |opts| 14 | opts.banner = "Usage: configure [options]" 15 | opts.on("-p", "--prefix PATH", "Set installation prefix") do |prefix| 16 | options[:prefix] = prefix 17 | end 18 | opts.on("-d","--debug", "Configure with debug options enabled") do |d| 19 | options[:debug] = d 20 | end 21 | opts.on_tail("-h", "--help", "Output usage summary") do 22 | puts opts 23 | exit 24 | end 25 | end.parse! 26 | 27 | additional_cmake_options = [] 28 | 29 | if options[:debug] 30 | puts "== Generating Debug Configuration" 31 | additional_cmake_options << "-D CMAKE_BUILD_TYPE=Debug" 32 | end 33 | 34 | if options[:prefix] 35 | additional_cmake_options << "-DCMAKE_INSTALL_PREFIX='#{options[:prefix]}'" 36 | end 37 | 38 | 39 | puts "== Removing Old Build Files" 40 | FileUtils.rm_rf("build") 41 | FileUtils.rm_f("Makefile") 42 | puts "== Running CMake in Build Directory" 43 | FileUtils.mkdir("build") 44 | FileUtils.cd("build") do 45 | # Check for cmake's existence 46 | if (!system("cmake > /dev/null")) 47 | puts "The \"cmake\" program is required to configure pillowtalk. It's" 48 | puts "available from most ports/packaging systems and http://cmake.org" 49 | exit 1 50 | end 51 | puts "cmake #{additional_cmake_options.join(' ')} .." 52 | if (!system("cmake #{additional_cmake_options.join(' ')} ..")) 53 | exit 1 54 | end 55 | end 56 | 57 | # now generate a Makefile 58 | puts "== Generating Makefile" 59 | File.open("Makefile", "w+") do |f| 60 | f.puts ".PHONY: all clean distclean install test distro" 61 | f.puts "all: distro" 62 | f.puts 63 | f.puts "distro:" 64 | f.puts " @cd build && make" 65 | f.puts 66 | f.puts "doc:" 67 | f.puts " @cd build && make doc" 68 | f.puts 69 | f.puts "test:" 70 | f.puts " @cd build && make && make test" 71 | f.puts 72 | f.puts "clean:" 73 | f.puts " @cd build && make clean" 74 | f.puts 75 | f.puts "distclean:" 76 | f.puts " @rm -rf Makefile build" 77 | f.puts 78 | f.puts "install: all" 79 | f.puts " @cd build && make && make install" 80 | f.puts 81 | end 82 | puts "== Configuration complete" 83 | -------------------------------------------------------------------------------- /examples/basic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file will just use the pillowtalk functions to build up some JSON 3 | * for a star wars document of the following JSON: 4 | * 5 | * 6 | * { 7 | * "_id": "star_wars", 8 | * "movies" : { 9 | * "Star Wars Episode IV": { 10 | * "characters": ["Luke Skywalker","Han Solo","Obi Wan Kenobi"] 11 | * } 12 | * } 13 | * } 14 | * 15 | * Then it should save that document in the pillowtalk_basics database at 16 | * localhost, and then retrieve and add a year key and add "Princess Leia" to 17 | * the characters list and save back. 18 | * 19 | */ 20 | #include 21 | #include 22 | #include 23 | 24 | #include "pillowtalk.h" 25 | 26 | int main(int argc,char** argv) 27 | { 28 | pt_init(); 29 | pt_node_t* root = pt_map_new(); 30 | pt_map_set(root,"_id",pt_string_new("star_wars")); 31 | 32 | pt_node_t* movies = pt_map_new(); 33 | pt_map_set(root,"movies",movies); 34 | 35 | // build movie subdocument 36 | pt_node_t* ep4 = pt_map_new(); 37 | 38 | // build characters array 39 | pt_node_t* ep4_chars = pt_array_new(); 40 | pt_array_push_back(ep4_chars,pt_string_new("Luke Skywalker")); 41 | pt_array_push_back(ep4_chars,pt_string_new("Han Solo")); 42 | pt_array_push_back(ep4_chars,pt_string_new("Obi Wan Kenobi")); 43 | 44 | pt_map_set(ep4,"characters", ep4_chars); 45 | 46 | pt_map_set(movies,"Star Wars Episode IV",ep4); 47 | 48 | pt_response_t* response = NULL; 49 | response = pt_delete("http://localhost:5984/pillowtalk_basics"); 50 | pt_free_response(response); 51 | response = pt_put("http://localhost:5984/pillowtalk_basics",NULL); 52 | pt_free_response(response); 53 | response = pt_put("http://localhost:5984/pillowtalk_basics/star_wars",root); 54 | assert(response->response_code == 201); 55 | pt_free_response(response); 56 | 57 | pt_free_node(root); 58 | 59 | response = pt_get("http://localhost:5984/pillowtalk_basics/star_wars"); 60 | assert(response->response_code == 200); 61 | 62 | pt_node_t* doc = response->root; 63 | const char* id = pt_string_get(pt_map_get(doc,"_id")); 64 | assert(!strcmp(id,"star_wars")); 65 | 66 | pt_node_t* ep4_node = pt_map_get(pt_map_get(doc,"movies"),"Star Wars Episode IV"); 67 | pt_node_t* characters_node = pt_map_get(ep4_node,"characters"); 68 | int array_len = pt_array_len(characters_node); 69 | assert(array_len == 3); 70 | 71 | pt_map_set(ep4_node,"year",pt_string_new("1977")); 72 | pt_array_push_back(characters_node,pt_string_new("Princess Leia")); 73 | pt_response_t* put_response = pt_put("http://localhost:5984/pillowtalk_basics/star_wars", doc); 74 | 75 | pt_free_response(response); 76 | pt_free_response(put_response); 77 | 78 | pt_cleanup(); 79 | } 80 | -------------------------------------------------------------------------------- /test/test_json_generator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define BOOST_TEST_MAIN 6 | #include 7 | #include "pillowtalk.h" 8 | 9 | using namespace std; 10 | using namespace boost::unit_test; 11 | 12 | static string 13 | read_file(const string& filename) 14 | { 15 | string test_files_dir = string(boost::unit_test::framework::master_test_suite().argv[1]); 16 | stringstream content; 17 | string line; 18 | ifstream myfile((test_files_dir + "/" + filename).c_str()); 19 | if (myfile.is_open()) 20 | { 21 | while (! myfile.eof() ) 22 | { 23 | getline (myfile,line); 24 | content << line << endl; 25 | } 26 | myfile.close(); 27 | } else { 28 | cout << "Unable to open file" << endl; 29 | exit(-1); 30 | } 31 | return content.str(); 32 | } 33 | 34 | BOOST_AUTO_TEST_CASE(test_null_json) 35 | { 36 | char* json_str = pt_to_json(NULL,0); 37 | BOOST_REQUIRE_EQUAL(json_str,"null"); 38 | free(json_str); 39 | } 40 | 41 | BOOST_AUTO_TEST_CASE(test_null_map_update) 42 | { 43 | pt_node_t* json = pt_from_json("{}"); 44 | int ret_code = pt_map_update(NULL,json,0); 45 | BOOST_REQUIRE_EQUAL(ret_code,1); 46 | pt_free_node(json); 47 | } 48 | 49 | BOOST_AUTO_TEST_CASE(test_basic_map_generation) 50 | { 51 | pt_node_t* map = pt_map_new(); 52 | pt_node_t* string = pt_string_new("world"); 53 | pt_map_set(map,"hello",string); 54 | char* json_str = pt_to_json(map,0); 55 | 56 | BOOST_REQUIRE_EQUAL(json_str,"{\"hello\":\"world\"}"); 57 | free(json_str); 58 | pt_free_node(map); 59 | } 60 | 61 | BOOST_AUTO_TEST_CASE(test_basic_array_generation) 62 | { 63 | pt_node_t* array = pt_array_new(); 64 | pt_node_t* integer = pt_integer_new(99); 65 | pt_node_t* dbl = pt_double_new(5.5); 66 | pt_node_t* string = pt_string_new("string"); 67 | pt_node_t* map = pt_map_new(); 68 | pt_node_t* world = pt_string_new("world"); 69 | pt_map_set(map,"hello",world); 70 | 71 | pt_array_push_back(array,integer); 72 | pt_array_push_back(array,dbl); 73 | pt_array_push_back(array,string); 74 | pt_array_push_back(array,map); 75 | 76 | char* json_str = pt_to_json(array,0); 77 | BOOST_REQUIRE_EQUAL(json_str,"[99,5.5,\"string\",{\"hello\":\"world\"}]"); 78 | 79 | free(json_str); 80 | pt_free_node(array); 81 | } 82 | 83 | BOOST_AUTO_TEST_CASE(test_clone) 84 | { 85 | char* star_wars = strdup(read_file("/fixtures/star_wars.json").c_str()); 86 | 87 | pt_node_t* star_wars_pt = pt_from_json(star_wars); 88 | pt_node_t* clone = pt_clone(star_wars_pt); 89 | 90 | char* star_wars_pt_str = pt_to_json(star_wars_pt,0); 91 | char* clone_str = pt_to_json(star_wars_pt,0); 92 | 93 | BOOST_REQUIRE_EQUAL(star_wars_pt_str,clone_str); 94 | 95 | free(star_wars); 96 | free(star_wars_pt_str); 97 | free(clone_str); 98 | 99 | pt_free_node(star_wars_pt); 100 | pt_free_node(clone); 101 | } 102 | 103 | BOOST_AUTO_TEST_CASE(update_map) 104 | { 105 | char* star_wars = strdup(read_file("/fixtures/star_wars.json").c_str()); 106 | char* star_wars_additions = strdup(read_file("/fixtures/star_wars_append.json").c_str()); 107 | char* star_wars_merged = strdup(read_file("/fixtures/star_wars_merged.json").c_str()); 108 | 109 | pt_node_t* star_wars_pt = pt_from_json(star_wars); 110 | pt_node_t* star_wars_additions_pt = pt_from_json(star_wars_additions); 111 | pt_node_t* star_wars_merged_pt = pt_from_json(star_wars_merged); 112 | 113 | free(star_wars); 114 | free(star_wars_additions); 115 | free(star_wars_merged); 116 | 117 | pt_map_update(star_wars_pt,star_wars_additions_pt, 1); 118 | pt_free_node(star_wars_additions_pt); 119 | 120 | char* computed_json = pt_to_json(star_wars_pt,0); 121 | char* merged_json = pt_to_json(star_wars_merged_pt,0); 122 | BOOST_REQUIRE_EQUAL(computed_json,merged_json); 123 | 124 | pt_free_node(star_wars_pt); 125 | pt_free_node(star_wars_merged_pt); 126 | free(computed_json); 127 | free(merged_json); 128 | } 129 | -------------------------------------------------------------------------------- /src/pillowtalk.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009, Curtis Spencer. All rights reserved. 3 | // 4 | #ifndef __PILLOWTALK__H_ 5 | #define __PILLOWTALK__H_ 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef enum {PT_MAP,PT_ARRAY,PT_NULL, PT_BOOLEAN, PT_INTEGER, PT_DOUBLE, PT_STRING, PT_KEY_VALUE} pt_type_t; 12 | 13 | typedef struct { 14 | pt_type_t type; 15 | } pt_node_t; 16 | 17 | typedef struct { 18 | pt_node_t* root; 19 | long response_code; 20 | char* raw_json; 21 | int raw_json_len; 22 | } pt_response_t; 23 | 24 | // Opaque type for iterator 25 | typedef struct { 26 | } pt_iterator_t; 27 | 28 | void pt_init(); 29 | void pt_cleanup(); 30 | 31 | void pt_free_node(pt_node_t* node); 32 | void pt_free_response(pt_response_t* res); 33 | 34 | /***** HTTP Related Functions ******/ 35 | 36 | pt_response_t* pt_delete(const char* server_target); 37 | 38 | pt_response_t* pt_put(const char* server_target, pt_node_t* document); 39 | pt_response_t* pt_put_raw(const char* server_target, const char* data, unsigned int data_len); 40 | 41 | /* 42 | * Do an HTTP get request on the target and parse the resulting JSON into the 43 | * pt_response object 44 | */ 45 | pt_response_t* pt_get(const char* server_target); 46 | 47 | 48 | /* 49 | * This will just do a get against the server target and not try to parse it at all. 50 | * It is useful for doing your own parsing with the resultant JSON 51 | */ 52 | pt_response_t* pt_unparsed_get(const char* server_target); 53 | 54 | /***** Node Related Functions ******/ 55 | 56 | /* 57 | * Once you have a node, you can call various functions on it, and most will 58 | * return NULL if you do it on the wrong type. Check the type attribute of the 59 | * pt_node_t to ensure you are doing the correct operation. 60 | */ 61 | pt_node_t* pt_map_get(pt_node_t* map,const char* key); 62 | 63 | unsigned int pt_array_len(pt_node_t* array); 64 | pt_node_t* pt_array_get(pt_node_t* array, unsigned int idx); 65 | 66 | int pt_is_null(pt_node_t* null); 67 | int pt_boolean_get(pt_node_t* boolean); 68 | int pt_integer_get(pt_node_t* integer); 69 | double pt_double_get(pt_node_t* dbl); 70 | const char* pt_string_get(pt_node_t* string); 71 | 72 | /* 73 | * The following functions are used to change a pt_node_t to do update 74 | * operations or get new json strings 75 | */ 76 | 77 | void pt_map_set(pt_node_t* map, const char* key, pt_node_t* value); 78 | void pt_map_unset(pt_node_t* map, const char* key); 79 | 80 | pt_node_t* pt_null_new(); 81 | pt_node_t* pt_bool_new(int boolean); 82 | pt_node_t* pt_integer_new(int integer); 83 | pt_node_t* pt_double_new(double dbl); 84 | pt_node_t* pt_string_new(const char* str); 85 | pt_node_t* pt_map_new(); 86 | pt_node_t* pt_array_new(); 87 | 88 | void pt_array_push_back(pt_node_t* array, pt_node_t* elem); 89 | void pt_array_push_front(pt_node_t* array, pt_node_t* elem); 90 | 91 | /* 92 | * This will remove elem if it exists in the array and free it as well, so 93 | * don't use elem after this 94 | */ 95 | void pt_array_remove(pt_node_t* array, pt_node_t* elem); 96 | 97 | /* 98 | * Build an iterator from an array/map node. If you pass in an unsupported 99 | * node it will return NULL 100 | */ 101 | pt_iterator_t* pt_iterator(pt_node_t* node); 102 | 103 | /* 104 | * This returns the next node in the iterator back and NULL when complete. 105 | * 106 | * The key char** is also set to the key of a key value pair if you are 107 | * iterating through a map 108 | * 109 | */ 110 | pt_node_t* pt_iterator_next(pt_iterator_t* iter, const char** key); 111 | 112 | 113 | /* 114 | * Convert a pt_node_t structure into a raw json string 115 | */ 116 | char* pt_to_json(pt_node_t* root, int beautify); 117 | 118 | /* 119 | * Take a raw json string and turn it into a pillowtalk structure 120 | */ 121 | pt_node_t* pt_from_json(const char* json); 122 | 123 | /* 124 | * Merge additions into an existing pt_node 125 | * 126 | * For example if your root looks like this 127 | * { 128 | * "name" : "Curtis", 129 | * "favorite_food" : "Bread" 130 | * } 131 | * 132 | * and your additions look like this 133 | * 134 | * { 135 | * "favorite_game" : "Street Fighter II" 136 | * } 137 | * 138 | * then the resulting json in root would be 139 | * 140 | * { 141 | * "name" : "Curtis", 142 | * "favorite_food" : "Bread", 143 | * "favorite_game" : "Street Fighter II" 144 | * } 145 | * 146 | * @return a nonzero error code if something cannot properly be merged. For 147 | * example, if a key in the root is an array and the additions has it as a hash 148 | * then it will give up there, but it won't rollback so be careful. 149 | */ 150 | int pt_map_update(pt_node_t* root, pt_node_t* additions,int append); 151 | 152 | /* 153 | * This method is useful if you want to clone a root you are working on to make 154 | * changes to it 155 | */ 156 | pt_node_t* pt_clone(pt_node_t* root); 157 | 158 | 159 | #ifdef __cplusplus 160 | } 161 | #endif 162 | 163 | #endif // _PILLOWTALK_H_ 164 | -------------------------------------------------------------------------------- /test/test_basic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define BOOST_TEST_MAIN 6 | #include 7 | #include "pillowtalk.h" 8 | 9 | using namespace std; 10 | using namespace boost::unit_test; 11 | 12 | struct InitFixture { 13 | InitFixture() { 14 | pt_init(); 15 | pt_response_t* res; 16 | 17 | res = pt_delete("http://localhost:5984/pt_test"); 18 | if (res->response_code == 500) { 19 | pt_free_response(res); 20 | cout << "Please ensure that couchdb is running on localhost" << endl; 21 | exit(-1); 22 | } 23 | pt_free_response(res); 24 | 25 | res = pt_put_raw("http://localhost:5984/pt_test",NULL,0); 26 | pt_free_response(res); 27 | 28 | const char* basic = "{}"; 29 | res = pt_put_raw("http://localhost:5984/pt_test/basic",basic,strlen(basic)); 30 | pt_free_response(res); 31 | 32 | const char* array = "{\"a\":[1,2,3]}"; 33 | res = pt_put_raw("http://localhost:5984/pt_test/array",array,strlen(array)); 34 | pt_free_response(res); 35 | } 36 | 37 | 38 | ~InitFixture() { 39 | pt_cleanup(); 40 | } 41 | }; 42 | 43 | //BOOST_FIXTURE_TEST_SUITE(s, InitFixture); 44 | BOOST_GLOBAL_FIXTURE(InitFixture); 45 | 46 | BOOST_AUTO_TEST_CASE( test_basic ) 47 | { 48 | pt_response_t* res = pt_get("http://localhost:5984/pt_test/basic"); 49 | BOOST_REQUIRE(res); 50 | BOOST_REQUIRE(res->root); 51 | BOOST_REQUIRE(res->root->type == PT_MAP); 52 | pt_node_t* id = pt_map_get(res->root,"_id"); 53 | BOOST_REQUIRE(id); 54 | BOOST_REQUIRE(id->type == PT_STRING); 55 | const char* val = pt_string_get(id); 56 | BOOST_REQUIRE_EQUAL(val,"basic"); 57 | 58 | pt_free_response(res); 59 | } 60 | 61 | BOOST_AUTO_TEST_CASE( test_array ) 62 | { 63 | pt_response_t* res = pt_get("http://localhost:5984/pt_test/array"); 64 | BOOST_REQUIRE(res->root); 65 | BOOST_REQUIRE(res->root->type == PT_MAP); 66 | 67 | pt_node_t* array = pt_map_get(res->root,"a"); 68 | BOOST_REQUIRE(array); 69 | BOOST_REQUIRE(array->type == PT_ARRAY); 70 | unsigned int len = pt_array_len(array); 71 | BOOST_REQUIRE_EQUAL(len,3); 72 | pt_free_response(res); 73 | } 74 | 75 | 76 | BOOST_AUTO_TEST_CASE( test_updates_to_document ) 77 | { 78 | pt_node_t* new_doc = pt_map_new(); 79 | pt_node_t* id = pt_string_new("mynewdoc"); 80 | pt_map_set(new_doc,"_id",id); 81 | 82 | pt_node_t* name = pt_string_new("jubos"); 83 | pt_map_set(new_doc,"name",name); 84 | pt_map_set(new_doc,"updates",pt_array_new()); 85 | 86 | pt_response_t* res = pt_put("http://localhost:5984/pt_test/mynewdoc",new_doc); 87 | BOOST_REQUIRE(pt_map_get(res->root,"rev") != NULL); 88 | pt_free_response(res); 89 | pt_free_node(new_doc); 90 | for(int i=0; i < 5; i++) { 91 | res = pt_get("http://localhost:5984/pt_test/mynewdoc"); 92 | BOOST_REQUIRE(res->response_code == 200); 93 | pt_array_push_back(pt_map_get(res->root,"updates"),pt_integer_new(i)); 94 | pt_response_t* put_res = pt_put("http://localhost:5984/pt_test/mynewdoc",res->root); 95 | BOOST_REQUIRE(put_res->response_code >= 200 && put_res->response_code < 300); 96 | pt_free_response(res); 97 | pt_free_response(put_res); 98 | } 99 | } 100 | 101 | // Here we make a new set of json and make sure we get what we expect 102 | BOOST_AUTO_TEST_CASE( test_mutable_json ) 103 | { 104 | pt_node_t* map = pt_map_new(); 105 | BOOST_REQUIRE(map); 106 | BOOST_REQUIRE(map->type == PT_MAP); 107 | 108 | pt_node_t* null = pt_null_new(); 109 | BOOST_REQUIRE(null); 110 | BOOST_REQUIRE(null->type == PT_NULL); 111 | 112 | pt_node_t* boolean = pt_bool_new(1); 113 | BOOST_REQUIRE(boolean); 114 | BOOST_REQUIRE(boolean->type == PT_BOOLEAN); 115 | 116 | pt_node_t* integer = pt_integer_new(100); 117 | BOOST_REQUIRE(integer); 118 | BOOST_REQUIRE(integer->type == PT_INTEGER); 119 | 120 | pt_node_t* dbl = pt_double_new(9.99); 121 | BOOST_REQUIRE(dbl); 122 | BOOST_REQUIRE(dbl->type == PT_DOUBLE); 123 | 124 | pt_node_t* world = pt_string_new("string"); 125 | BOOST_REQUIRE(world); 126 | BOOST_REQUIRE(world->type == PT_STRING); 127 | 128 | pt_node_t* ary = pt_array_new(); 129 | BOOST_REQUIRE(ary); 130 | BOOST_REQUIRE(ary->type == PT_ARRAY); 131 | 132 | pt_node_t* int1 = pt_integer_new(1); 133 | pt_node_t* int2 = pt_integer_new(2); 134 | pt_node_t* int3 = pt_integer_new(3); 135 | pt_array_push_back(ary,int1); 136 | pt_array_push_back(ary,int2); 137 | pt_array_push_back(ary,int3); 138 | 139 | BOOST_REQUIRE_EQUAL(pt_array_len(ary),3); 140 | 141 | pt_array_remove(ary,int2); 142 | 143 | BOOST_REQUIRE_EQUAL(pt_array_len(ary),2); 144 | 145 | 146 | // Now set these things into the map 147 | 148 | pt_map_set(map,"null",null); 149 | pt_map_set(map,"boolean",boolean); 150 | pt_map_set(map,"integer",integer); 151 | pt_map_set(map,"double",dbl); 152 | pt_map_set(map,"string",world); 153 | pt_map_set(map,"array",ary); 154 | 155 | pt_node_t* value = pt_map_get(map,"null"); 156 | BOOST_REQUIRE(value->type == PT_NULL); 157 | 158 | value = pt_map_get(map,"boolean"); 159 | BOOST_REQUIRE(pt_boolean_get(value) == 1); 160 | 161 | value = pt_map_get(map,"integer"); 162 | BOOST_REQUIRE(pt_integer_get(value) == 100); 163 | 164 | value = pt_map_get(map,"double"); 165 | BOOST_REQUIRE(pt_double_get(value) == 9.99); 166 | 167 | value = pt_map_get(map,"string"); 168 | BOOST_REQUIRE(value); 169 | BOOST_REQUIRE_EQUAL("string",pt_string_get(value)); 170 | 171 | value = pt_map_get(map,"array"); 172 | BOOST_REQUIRE(value); 173 | 174 | 175 | pt_map_unset(map,"string"); 176 | BOOST_REQUIRE(!pt_map_get(map,"string")); 177 | 178 | pt_free_node(map); 179 | } 180 | 181 | //BOOST_AUTO_TEST_SUITE_END() 182 | -------------------------------------------------------------------------------- /src/utlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2009, Troy D. Hanson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef UTLIST_H 25 | #define UTLIST_H 26 | 27 | #define UTLIST_VERSION 1.0 28 | 29 | /* C++ requires extra stringent casting */ 30 | #if defined __cplusplus 31 | #define LTYPEOF(x) (typeof(x)) 32 | #else 33 | #define LTYPEOF(x) 34 | #endif 35 | /* 36 | * This file contains macros to manipulate singly and doubly-linked lists. 37 | * 38 | * 1. LL_ macros: singly-linked lists. 39 | * 2. DL_ macros: doubly-linked lists. 40 | * 3. CDL_ macros: circular doubly-linked lists. 41 | * 42 | * To use singly-linked lists, your structure must have a "next" pointer. 43 | * To use doubly-linked lists, your structure must "prev" and "next" pointers. 44 | * Either way, the pointer to the head of the list must be initialized to NULL. 45 | * 46 | * ----------------.EXAMPLE ------------------------- 47 | * struct item { 48 | * int id; 49 | * struct item *prev, *next; 50 | * } 51 | * 52 | * struct item *list = NULL: 53 | * 54 | * int main() { 55 | * struct item *item; 56 | * ... allocate and populate item ... 57 | * DL_APPEND(list, item); 58 | * } 59 | * -------------------------------------------------- 60 | * 61 | * For doubly-linked lists, the append and delete macros are O(1) 62 | * For singly-linked lists, append and delete are O(n) but prepend is O(1) 63 | * The sort macro is O(n log(n)) for all types of single/double/circular lists. 64 | */ 65 | 66 | /****************************************************************************** 67 | * The SORT macros * 68 | *****************************************************************************/ 69 | #define LL_SORT(l,cmp) \ 70 | LISTSORT(l,0,0,FIELD_OFFSET(l,next),cmp) 71 | #define DL_SORT(l,cmp) \ 72 | LISTSORT(l,0,FIELD_OFFSET(l,prev),FIELD_OFFSET(l,next),cmp) 73 | #define CDL_SORT(l,cmp) \ 74 | LISTSORT(l,1,FIELD_OFFSET(l,prev),FIELD_OFFSET(l,next),cmp) 75 | 76 | /* The macros can't assume or cast to the caller's list element type. So we use 77 | * a couple tricks when we need to deal with those element's prev/next pointers. 78 | * Basically we use char pointer arithmetic to get those field offsets. */ 79 | #define FIELD_OFFSET(ptr,field) ((char*)&((ptr)->field) - (char*)(ptr)) 80 | #define LNEXT(e,no) (*(char**)(((char*)e) + no)) 81 | #define LPREV(e,po) (*(char**)(((char*)e) + po)) 82 | /****************************************************************************** 83 | * The LISTSORT macro is an adaptation of Simon Tatham's O(n log(n)) mergesort* 84 | * Unwieldy variable names used here to avoid shadowing passed-in variables. * 85 | *****************************************************************************/ 86 | #define LISTSORT(list, is_circular, po, no, cmp) \ 87 | do { \ 88 | void *_ls_p, *_ls_q, *_ls_e, *_ls_tail, *_ls_oldhead; \ 89 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 90 | int _ls_is_double = (po==0) ? 0 : 1; \ 91 | if (list) { \ 92 | _ls_insize = 1; \ 93 | _ls_looping = 1; \ 94 | while (_ls_looping) { \ 95 | _ls_p = list; \ 96 | _ls_oldhead = list; \ 97 | list = NULL; \ 98 | _ls_tail = NULL; \ 99 | _ls_nmerges = 0; \ 100 | while (_ls_p) { \ 101 | _ls_nmerges++; \ 102 | _ls_q = _ls_p; \ 103 | _ls_psize = 0; \ 104 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 105 | _ls_psize++; \ 106 | if (is_circular) { \ 107 | _ls_q = ((LNEXT(_ls_q,no) == _ls_oldhead) ? NULL : LNEXT(_ls_q,no)); \ 108 | } else { \ 109 | _ls_q = LNEXT(_ls_q,no); \ 110 | } \ 111 | if (!_ls_q) break; \ 112 | } \ 113 | _ls_qsize = _ls_insize; \ 114 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 115 | if (_ls_psize == 0) { \ 116 | _ls_e = _ls_q; _ls_q = LNEXT(_ls_q,no); _ls_qsize--; \ 117 | if (is_circular && _ls_q == _ls_oldhead) { _ls_q = NULL; } \ 118 | } else if (_ls_qsize == 0 || !_ls_q) { \ 119 | _ls_e = _ls_p; _ls_p = LNEXT(_ls_p,no); _ls_psize--; \ 120 | if (is_circular && (_ls_p == _ls_oldhead)) { _ls_p = NULL; } \ 121 | } else if (cmp(LTYPEOF(list)_ls_p,LTYPEOF(list)_ls_q) <= 0) { \ 122 | _ls_e = _ls_p; _ls_p = LNEXT(_ls_p,no); _ls_psize--; \ 123 | if (is_circular && (_ls_p == _ls_oldhead)) { _ls_p = NULL; } \ 124 | } else { \ 125 | _ls_e = _ls_q; _ls_q = LNEXT(_ls_q,no); _ls_qsize--; \ 126 | if (is_circular && (_ls_q == _ls_oldhead)) { _ls_q = NULL; } \ 127 | } \ 128 | if (_ls_tail) { \ 129 | LNEXT(_ls_tail,no) = (char*)_ls_e; \ 130 | } else { \ 131 | list = LTYPEOF(list)_ls_e; \ 132 | } \ 133 | if (_ls_is_double) { \ 134 | LPREV(_ls_e,po) = (char*)_ls_tail; \ 135 | } \ 136 | _ls_tail = _ls_e; \ 137 | } \ 138 | _ls_p = _ls_q; \ 139 | } \ 140 | if (is_circular) { \ 141 | LNEXT(_ls_tail,no) = (char*)list; \ 142 | if (_ls_is_double) { \ 143 | LPREV(list,po) = (char*)_ls_tail; \ 144 | } \ 145 | } else { \ 146 | LNEXT(_ls_tail,no) = NULL; \ 147 | } \ 148 | if (_ls_nmerges <= 1) { \ 149 | _ls_looping=0; \ 150 | } \ 151 | _ls_insize *= 2; \ 152 | } \ 153 | } \ 154 | } while (0) 155 | 156 | /****************************************************************************** 157 | * singly linked list macros (non-circular) * 158 | *****************************************************************************/ 159 | #define LL_PREPEND(head,add) \ 160 | do { \ 161 | (add)->next = head; \ 162 | head = add; \ 163 | } while (0) 164 | 165 | #define LL_APPEND(head,add) \ 166 | do { \ 167 | (add)->next=NULL; \ 168 | if (head) { \ 169 | char *_lla_el = (char*)(head); \ 170 | unsigned _lla_no = FIELD_OFFSET(head,next); \ 171 | while (LNEXT(_lla_el,_lla_no)) { _lla_el = LNEXT(_lla_el,_lla_no); } \ 172 | LNEXT(_lla_el,_lla_no)=(char*)(add); \ 173 | } else { \ 174 | (head)=(add); \ 175 | } \ 176 | } while (0) 177 | 178 | #define LL_DELETE(head,del) \ 179 | do { \ 180 | if ((head) == (del)) { \ 181 | (head)=(head)->next; \ 182 | } else { \ 183 | char *_lld_el = (char*)(head); \ 184 | unsigned _lld_no = FIELD_OFFSET(head,next); \ 185 | while (LNEXT(_lld_el,_lld_no) && (LNEXT(_lld_el,_lld_no) != (char*)(del))) { \ 186 | _lld_el = LNEXT(_lld_el,_lld_no); \ 187 | } \ 188 | if (LNEXT(_lld_el,_lld_no)) { \ 189 | LNEXT(_lld_el,_lld_no) = (char*)((del)->next); \ 190 | } \ 191 | } \ 192 | } while (0) 193 | 194 | #define LL_FOREACH(head,el) \ 195 | for(el=head;el;el=el->next) 196 | 197 | /****************************************************************************** 198 | * doubly linked list macros (non-circular) * 199 | *****************************************************************************/ 200 | #define DL_PREPEND(head,add) \ 201 | do { \ 202 | (add)->next = head; \ 203 | if (head) { \ 204 | (add)->prev = (head)->prev; \ 205 | (head)->prev = (add); \ 206 | } else { \ 207 | (add)->prev = (add); \ 208 | } \ 209 | (head) = (add); \ 210 | } while (0) 211 | 212 | #define DL_APPEND(head,add) \ 213 | do { \ 214 | if (head) { \ 215 | (add)->prev = (head)->prev; \ 216 | (head)->prev->next = (add); \ 217 | (head)->prev = (add); \ 218 | (add)->next = NULL; \ 219 | } else { \ 220 | (head)=(add); \ 221 | (head)->prev = (head); \ 222 | (head)->next = NULL; \ 223 | } \ 224 | } while (0); 225 | 226 | #define DL_DELETE(head,del) \ 227 | do { \ 228 | if ((del)->prev == (del)) { \ 229 | (head)=NULL; \ 230 | } else if ((del)==(head)) { \ 231 | (del)->next->prev = (del)->prev; \ 232 | (head) = (del)->next; \ 233 | } else { \ 234 | (del)->prev->next = (del)->next; \ 235 | if ((del)->next) { \ 236 | (del)->next->prev = (del)->prev; \ 237 | } else { \ 238 | (head)->prev = (del)->prev; \ 239 | } \ 240 | } \ 241 | } while (0); 242 | 243 | 244 | #define DL_FOREACH(head,el) \ 245 | for(el=head;el;el=el->next) 246 | 247 | /****************************************************************************** 248 | * circular doubly linked list macros * 249 | *****************************************************************************/ 250 | #define CDL_PREPEND(head,add) \ 251 | do { \ 252 | if (head) { \ 253 | (add)->prev = (head)->prev; \ 254 | (add)->next = (head); \ 255 | (head)->prev = (add); \ 256 | (add)->prev->next = (add); \ 257 | } else { \ 258 | (add)->prev = (add); \ 259 | (add)->next = (add); \ 260 | } \ 261 | (head)=(add); \ 262 | } while (0) 263 | 264 | #define CDL_DELETE(head,del) \ 265 | do { \ 266 | if ( ((head)==(del)) && ((head)->next == (head))) { \ 267 | (head) = 0L; \ 268 | } else { \ 269 | (del)->next->prev = (del)->prev; \ 270 | (del)->prev->next = (del)->next; \ 271 | if ((del) == (head)) (head)=(del)->next; \ 272 | } \ 273 | } while (0); 274 | 275 | #define CDL_FOREACH(head,el) \ 276 | for(el=head;el;el= (el->next==head ? 0L : el->next)) 277 | 278 | 279 | #endif /* UTLIST_H */ 280 | 281 | -------------------------------------------------------------------------------- /src/bsd_queue.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1991, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 4. Neither the name of the University nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * @(#)queue.h 8.5 (Berkeley) 8/20/94 30 | * $FreeBSD$ 31 | */ 32 | 33 | #ifndef _SYS_QUEUE_H_ 34 | #define _SYS_QUEUE_H_ 35 | 36 | #include 37 | 38 | /* 39 | * This file defines four types of data structures: singly-linked lists, 40 | * singly-linked tail queues, lists and tail queues. 41 | * 42 | * A singly-linked list is headed by a single forward pointer. The elements 43 | * are singly linked for minimum space and pointer manipulation overhead at 44 | * the expense of O(n) removal for arbitrary elements. New elements can be 45 | * added to the list after an existing element or at the head of the list. 46 | * Elements being removed from the head of the list should use the explicit 47 | * macro for this purpose for optimum efficiency. A singly-linked list may 48 | * only be traversed in the forward direction. Singly-linked lists are ideal 49 | * for applications with large datasets and few or no removals or for 50 | * implementing a LIFO queue. 51 | * 52 | * A singly-linked tail queue is headed by a pair of pointers, one to the 53 | * head of the list and the other to the tail of the list. The elements are 54 | * singly linked for minimum space and pointer manipulation overhead at the 55 | * expense of O(n) removal for arbitrary elements. New elements can be added 56 | * to the list after an existing element, at the head of the list, or at the 57 | * end of the list. Elements being removed from the head of the tail queue 58 | * should use the explicit macro for this purpose for optimum efficiency. 59 | * A singly-linked tail queue may only be traversed in the forward direction. 60 | * Singly-linked tail queues are ideal for applications with large datasets 61 | * and few or no removals or for implementing a FIFO queue. 62 | * 63 | * A list is headed by a single forward pointer (or an array of forward 64 | * pointers for a hash table header). The elements are doubly linked 65 | * so that an arbitrary element can be removed without a need to 66 | * traverse the list. New elements can be added to the list before 67 | * or after an existing element or at the head of the list. A list 68 | * may only be traversed in the forward direction. 69 | * 70 | * A tail queue is headed by a pair of pointers, one to the head of the 71 | * list and the other to the tail of the list. The elements are doubly 72 | * linked so that an arbitrary element can be removed without a need to 73 | * traverse the list. New elements can be added to the list before or 74 | * after an existing element, at the head of the list, or at the end of 75 | * the list. A tail queue may be traversed in either direction. 76 | * 77 | * For details on the use of these macros, see the queue(3) manual page. 78 | * 79 | * 80 | * SLIST LIST STAILQ TAILQ 81 | * _HEAD + + + + 82 | * _HEAD_INITIALIZER + + + + 83 | * _ENTRY + + + + 84 | * _INIT + + + + 85 | * _EMPTY + + + + 86 | * _FIRST + + + + 87 | * _NEXT + + + + 88 | * _PREV - - - + 89 | * _LAST - - + + 90 | * _FOREACH + + + + 91 | * _FOREACH_SAFE + + + + 92 | * _FOREACH_REVERSE - - - + 93 | * _FOREACH_REVERSE_SAFE - - - + 94 | * _INSERT_HEAD + + + + 95 | * _INSERT_BEFORE - + - + 96 | * _INSERT_AFTER + + + + 97 | * _INSERT_TAIL - - + + 98 | * _CONCAT - - + + 99 | * _REMOVE_HEAD + - + - 100 | * _REMOVE + + + + 101 | * 102 | */ 103 | #ifdef QUEUE_MACRO_DEBUG 104 | /* Store the last 2 places the queue element or head was altered */ 105 | struct qm_trace { 106 | char * lastfile; 107 | int lastline; 108 | char * prevfile; 109 | int prevline; 110 | }; 111 | 112 | #define TRACEBUF struct qm_trace trace; 113 | #define TRASHIT(x) do {(x) = (void *)-1;} while (0) 114 | 115 | #define QMD_TRACE_HEAD(head) do { \ 116 | (head)->trace.prevline = (head)->trace.lastline; \ 117 | (head)->trace.prevfile = (head)->trace.lastfile; \ 118 | (head)->trace.lastline = __LINE__; \ 119 | (head)->trace.lastfile = __FILE__; \ 120 | } while (0) 121 | 122 | #define QMD_TRACE_ELEM(elem) do { \ 123 | (elem)->trace.prevline = (elem)->trace.lastline; \ 124 | (elem)->trace.prevfile = (elem)->trace.lastfile; \ 125 | (elem)->trace.lastline = __LINE__; \ 126 | (elem)->trace.lastfile = __FILE__; \ 127 | } while (0) 128 | 129 | #else 130 | #define QMD_TRACE_ELEM(elem) 131 | #define QMD_TRACE_HEAD(head) 132 | #define TRACEBUF 133 | #define TRASHIT(x) 134 | #endif /* QUEUE_MACRO_DEBUG */ 135 | 136 | /* 137 | * Singly-linked List declarations. 138 | */ 139 | #define SLIST_HEAD(name, type) \ 140 | struct name { \ 141 | struct type *slh_first; /* first element */ \ 142 | } 143 | 144 | #define SLIST_HEAD_INITIALIZER(head) \ 145 | { NULL } 146 | 147 | #define SLIST_ENTRY(type) \ 148 | struct { \ 149 | struct type *sle_next; /* next element */ \ 150 | } 151 | 152 | /* 153 | * Singly-linked List functions. 154 | */ 155 | #define SLIST_EMPTY(head) ((head)->slh_first == NULL) 156 | 157 | #define SLIST_FIRST(head) ((head)->slh_first) 158 | 159 | #define SLIST_FOREACH(var, head, field) \ 160 | for ((var) = SLIST_FIRST((head)); \ 161 | (var); \ 162 | (var) = SLIST_NEXT((var), field)) 163 | 164 | #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ 165 | for ((var) = SLIST_FIRST((head)); \ 166 | (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ 167 | (var) = (tvar)) 168 | 169 | #define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ 170 | for ((varp) = &SLIST_FIRST((head)); \ 171 | ((var) = *(varp)) != NULL; \ 172 | (varp) = &SLIST_NEXT((var), field)) 173 | 174 | #define SLIST_INIT(head) do { \ 175 | SLIST_FIRST((head)) = NULL; \ 176 | } while (0) 177 | 178 | #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ 179 | SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ 180 | SLIST_NEXT((slistelm), field) = (elm); \ 181 | } while (0) 182 | 183 | #define SLIST_INSERT_HEAD(head, elm, field) do { \ 184 | SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ 185 | SLIST_FIRST((head)) = (elm); \ 186 | } while (0) 187 | 188 | #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) 189 | 190 | #define SLIST_REMOVE(head, elm, type, field) do { \ 191 | if (SLIST_FIRST((head)) == (elm)) { \ 192 | SLIST_REMOVE_HEAD((head), field); \ 193 | } \ 194 | else { \ 195 | struct type *curelm = SLIST_FIRST((head)); \ 196 | while (SLIST_NEXT(curelm, field) != (elm)) \ 197 | curelm = SLIST_NEXT(curelm, field); \ 198 | SLIST_NEXT(curelm, field) = \ 199 | SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ 200 | } \ 201 | TRASHIT((elm)->field.sle_next); \ 202 | } while (0) 203 | 204 | #define SLIST_REMOVE_HEAD(head, field) do { \ 205 | SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ 206 | } while (0) 207 | 208 | /* 209 | * Singly-linked Tail queue declarations. 210 | */ 211 | #define STAILQ_HEAD(name, type) \ 212 | struct name { \ 213 | struct type *stqh_first;/* first element */ \ 214 | struct type **stqh_last;/* addr of last next element */ \ 215 | } 216 | 217 | #define STAILQ_HEAD_INITIALIZER(head) \ 218 | { NULL, &(head).stqh_first } 219 | 220 | #define STAILQ_ENTRY(type) \ 221 | struct { \ 222 | struct type *stqe_next; /* next element */ \ 223 | } 224 | 225 | /* 226 | * Singly-linked Tail queue functions. 227 | */ 228 | #define STAILQ_CONCAT(head1, head2) do { \ 229 | if (!STAILQ_EMPTY((head2))) { \ 230 | *(head1)->stqh_last = (head2)->stqh_first; \ 231 | (head1)->stqh_last = (head2)->stqh_last; \ 232 | STAILQ_INIT((head2)); \ 233 | } \ 234 | } while (0) 235 | 236 | #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) 237 | 238 | #define STAILQ_FIRST(head) ((head)->stqh_first) 239 | 240 | #define STAILQ_FOREACH(var, head, field) \ 241 | for((var) = STAILQ_FIRST((head)); \ 242 | (var); \ 243 | (var) = STAILQ_NEXT((var), field)) 244 | 245 | 246 | #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ 247 | for ((var) = STAILQ_FIRST((head)); \ 248 | (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ 249 | (var) = (tvar)) 250 | 251 | #define STAILQ_INIT(head) do { \ 252 | STAILQ_FIRST((head)) = NULL; \ 253 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 254 | } while (0) 255 | 256 | #define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ 257 | if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ 258 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 259 | STAILQ_NEXT((tqelm), field) = (elm); \ 260 | } while (0) 261 | 262 | #define STAILQ_INSERT_HEAD(head, elm, field) do { \ 263 | if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ 264 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 265 | STAILQ_FIRST((head)) = (elm); \ 266 | } while (0) 267 | 268 | #define STAILQ_INSERT_TAIL(head, elm, field) do { \ 269 | STAILQ_NEXT((elm), field) = NULL; \ 270 | *(head)->stqh_last = (elm); \ 271 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 272 | } while (0) 273 | 274 | #define STAILQ_LAST(head, type, field) \ 275 | (STAILQ_EMPTY((head)) ? \ 276 | NULL : \ 277 | ((struct type *)(void *) \ 278 | ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) 279 | 280 | #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) 281 | 282 | #define STAILQ_REMOVE(head, elm, type, field) do { \ 283 | if (STAILQ_FIRST((head)) == (elm)) { \ 284 | STAILQ_REMOVE_HEAD((head), field); \ 285 | } \ 286 | else { \ 287 | struct type *curelm = STAILQ_FIRST((head)); \ 288 | while (STAILQ_NEXT(curelm, field) != (elm)) \ 289 | curelm = STAILQ_NEXT(curelm, field); \ 290 | if ((STAILQ_NEXT(curelm, field) = \ 291 | STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\ 292 | (head)->stqh_last = &STAILQ_NEXT((curelm), field);\ 293 | } \ 294 | TRASHIT((elm)->field.stqe_next); \ 295 | } while (0) 296 | 297 | #define STAILQ_REMOVE_HEAD(head, field) do { \ 298 | if ((STAILQ_FIRST((head)) = \ 299 | STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ 300 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 301 | } while (0) 302 | 303 | /* 304 | * List declarations. 305 | */ 306 | #define LIST_HEAD(name, type) \ 307 | struct name { \ 308 | struct type *lh_first; /* first element */ \ 309 | } 310 | 311 | #define LIST_HEAD_INITIALIZER(head) \ 312 | { NULL } 313 | 314 | #define LIST_ENTRY(type) \ 315 | struct { \ 316 | struct type *le_next; /* next element */ \ 317 | struct type **le_prev; /* address of previous next element */ \ 318 | } 319 | 320 | /* 321 | * List functions. 322 | */ 323 | 324 | #if (defined(_KERNEL) && defined(INVARIANTS)) 325 | #define QMD_LIST_CHECK_HEAD(head, field) do { \ 326 | if (LIST_FIRST((head)) != NULL && \ 327 | LIST_FIRST((head))->field.le_prev != \ 328 | &LIST_FIRST((head))) \ 329 | panic("Bad list head %p first->prev != head", (head)); \ 330 | } while (0) 331 | 332 | #define QMD_LIST_CHECK_NEXT(elm, field) do { \ 333 | if (LIST_NEXT((elm), field) != NULL && \ 334 | LIST_NEXT((elm), field)->field.le_prev != \ 335 | &((elm)->field.le_next)) \ 336 | panic("Bad link elm %p next->prev != elm", (elm)); \ 337 | } while (0) 338 | 339 | #define QMD_LIST_CHECK_PREV(elm, field) do { \ 340 | if (*(elm)->field.le_prev != (elm)) \ 341 | panic("Bad link elm %p prev->next != elm", (elm)); \ 342 | } while (0) 343 | #else 344 | #define QMD_LIST_CHECK_HEAD(head, field) 345 | #define QMD_LIST_CHECK_NEXT(elm, field) 346 | #define QMD_LIST_CHECK_PREV(elm, field) 347 | #endif /* (_KERNEL && INVARIANTS) */ 348 | 349 | #define LIST_EMPTY(head) ((head)->lh_first == NULL) 350 | 351 | #define LIST_FIRST(head) ((head)->lh_first) 352 | 353 | #define LIST_FOREACH(var, head, field) \ 354 | for ((var) = LIST_FIRST((head)); \ 355 | (var); \ 356 | (var) = LIST_NEXT((var), field)) 357 | 358 | #define LIST_FOREACH_SAFE(var, head, field, tvar) \ 359 | for ((var) = LIST_FIRST((head)); \ 360 | (var) && ((tvar) = LIST_NEXT((var), field), 1); \ 361 | (var) = (tvar)) 362 | 363 | #define LIST_INIT(head) do { \ 364 | LIST_FIRST((head)) = NULL; \ 365 | } while (0) 366 | 367 | #define LIST_INSERT_AFTER(listelm, elm, field) do { \ 368 | QMD_LIST_CHECK_NEXT(listelm, field); \ 369 | if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ 370 | LIST_NEXT((listelm), field)->field.le_prev = \ 371 | &LIST_NEXT((elm), field); \ 372 | LIST_NEXT((listelm), field) = (elm); \ 373 | (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ 374 | } while (0) 375 | 376 | #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ 377 | QMD_LIST_CHECK_PREV(listelm, field); \ 378 | (elm)->field.le_prev = (listelm)->field.le_prev; \ 379 | LIST_NEXT((elm), field) = (listelm); \ 380 | *(listelm)->field.le_prev = (elm); \ 381 | (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ 382 | } while (0) 383 | 384 | #define LIST_INSERT_HEAD(head, elm, field) do { \ 385 | QMD_LIST_CHECK_HEAD((head), field); \ 386 | if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ 387 | LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ 388 | LIST_FIRST((head)) = (elm); \ 389 | (elm)->field.le_prev = &LIST_FIRST((head)); \ 390 | } while (0) 391 | 392 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) 393 | 394 | #define LIST_REMOVE(elm, field) do { \ 395 | QMD_LIST_CHECK_NEXT(elm, field); \ 396 | QMD_LIST_CHECK_PREV(elm, field); \ 397 | if (LIST_NEXT((elm), field) != NULL) \ 398 | LIST_NEXT((elm), field)->field.le_prev = \ 399 | (elm)->field.le_prev; \ 400 | *(elm)->field.le_prev = LIST_NEXT((elm), field); \ 401 | TRASHIT((elm)->field.le_next); \ 402 | TRASHIT((elm)->field.le_prev); \ 403 | } while (0) 404 | 405 | /* 406 | * Tail queue declarations. 407 | */ 408 | #define TAILQ_HEAD(name, type) \ 409 | struct name { \ 410 | struct type *tqh_first; /* first element */ \ 411 | struct type **tqh_last; /* addr of last next element */ \ 412 | TRACEBUF \ 413 | } 414 | 415 | #define TAILQ_HEAD_INITIALIZER(head) \ 416 | { NULL, &(head).tqh_first } 417 | 418 | #define TAILQ_ENTRY(type) \ 419 | struct { \ 420 | struct type *tqe_next; /* next element */ \ 421 | struct type **tqe_prev; /* address of previous next element */ \ 422 | TRACEBUF \ 423 | } 424 | 425 | /* 426 | * Tail queue functions. 427 | */ 428 | #if (defined(_KERNEL) && defined(INVARIANTS)) 429 | #define QMD_TAILQ_CHECK_HEAD(head, field) do { \ 430 | if (!TAILQ_EMPTY(head) && \ 431 | TAILQ_FIRST((head))->field.tqe_prev != \ 432 | &TAILQ_FIRST((head))) \ 433 | panic("Bad tailq head %p first->prev != head", (head)); \ 434 | } while (0) 435 | 436 | #define QMD_TAILQ_CHECK_TAIL(head, field) do { \ 437 | if (*(head)->tqh_last != NULL) \ 438 | panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ 439 | } while (0) 440 | 441 | #define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ 442 | if (TAILQ_NEXT((elm), field) != NULL && \ 443 | TAILQ_NEXT((elm), field)->field.tqe_prev != \ 444 | &((elm)->field.tqe_next)) \ 445 | panic("Bad link elm %p next->prev != elm", (elm)); \ 446 | } while (0) 447 | 448 | #define QMD_TAILQ_CHECK_PREV(elm, field) do { \ 449 | if (*(elm)->field.tqe_prev != (elm)) \ 450 | panic("Bad link elm %p prev->next != elm", (elm)); \ 451 | } while (0) 452 | #else 453 | #define QMD_TAILQ_CHECK_HEAD(head, field) 454 | #define QMD_TAILQ_CHECK_TAIL(head, headname) 455 | #define QMD_TAILQ_CHECK_NEXT(elm, field) 456 | #define QMD_TAILQ_CHECK_PREV(elm, field) 457 | #endif /* (_KERNEL && INVARIANTS) */ 458 | 459 | #define TAILQ_CONCAT(head1, head2, field) do { \ 460 | if (!TAILQ_EMPTY(head2)) { \ 461 | *(head1)->tqh_last = (head2)->tqh_first; \ 462 | (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ 463 | (head1)->tqh_last = (head2)->tqh_last; \ 464 | TAILQ_INIT((head2)); \ 465 | QMD_TRACE_HEAD(head1); \ 466 | QMD_TRACE_HEAD(head2); \ 467 | } \ 468 | } while (0) 469 | 470 | #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) 471 | 472 | #define TAILQ_FIRST(head) ((head)->tqh_first) 473 | 474 | #define TAILQ_FOREACH(var, head, field) \ 475 | for ((var) = TAILQ_FIRST((head)); \ 476 | (var); \ 477 | (var) = TAILQ_NEXT((var), field)) 478 | 479 | #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ 480 | for ((var) = TAILQ_FIRST((head)); \ 481 | (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ 482 | (var) = (tvar)) 483 | 484 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ 485 | for ((var) = TAILQ_LAST((head), headname); \ 486 | (var); \ 487 | (var) = TAILQ_PREV((var), headname, field)) 488 | 489 | #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ 490 | for ((var) = TAILQ_LAST((head), headname); \ 491 | (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ 492 | (var) = (tvar)) 493 | 494 | #define TAILQ_INIT(head) do { \ 495 | TAILQ_FIRST((head)) = NULL; \ 496 | (head)->tqh_last = &TAILQ_FIRST((head)); \ 497 | QMD_TRACE_HEAD(head); \ 498 | } while (0) 499 | 500 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ 501 | QMD_TAILQ_CHECK_NEXT(listelm, field); \ 502 | if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ 503 | TAILQ_NEXT((elm), field)->field.tqe_prev = \ 504 | &TAILQ_NEXT((elm), field); \ 505 | else { \ 506 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 507 | QMD_TRACE_HEAD(head); \ 508 | } \ 509 | TAILQ_NEXT((listelm), field) = (elm); \ 510 | (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ 511 | QMD_TRACE_ELEM(&(elm)->field); \ 512 | QMD_TRACE_ELEM(&listelm->field); \ 513 | } while (0) 514 | 515 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ 516 | QMD_TAILQ_CHECK_PREV(listelm, field); \ 517 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ 518 | TAILQ_NEXT((elm), field) = (listelm); \ 519 | *(listelm)->field.tqe_prev = (elm); \ 520 | (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ 521 | QMD_TRACE_ELEM(&(elm)->field); \ 522 | QMD_TRACE_ELEM(&listelm->field); \ 523 | } while (0) 524 | 525 | #define TAILQ_INSERT_HEAD(head, elm, field) do { \ 526 | QMD_TAILQ_CHECK_HEAD(head, field); \ 527 | if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ 528 | TAILQ_FIRST((head))->field.tqe_prev = \ 529 | &TAILQ_NEXT((elm), field); \ 530 | else \ 531 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 532 | TAILQ_FIRST((head)) = (elm); \ 533 | (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ 534 | QMD_TRACE_HEAD(head); \ 535 | QMD_TRACE_ELEM(&(elm)->field); \ 536 | } while (0) 537 | 538 | #define TAILQ_INSERT_TAIL(head, elm, field) do { \ 539 | QMD_TAILQ_CHECK_TAIL(head, field); \ 540 | TAILQ_NEXT((elm), field) = NULL; \ 541 | (elm)->field.tqe_prev = (head)->tqh_last; \ 542 | *(head)->tqh_last = (elm); \ 543 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 544 | QMD_TRACE_HEAD(head); \ 545 | QMD_TRACE_ELEM(&(elm)->field); \ 546 | } while (0) 547 | 548 | #define TAILQ_LAST(head, headname) \ 549 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) 550 | 551 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) 552 | 553 | #define TAILQ_PREV(elm, headname, field) \ 554 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) 555 | 556 | #define TAILQ_REMOVE(head, elm, field) do { \ 557 | QMD_TAILQ_CHECK_NEXT(elm, field); \ 558 | QMD_TAILQ_CHECK_PREV(elm, field); \ 559 | if ((TAILQ_NEXT((elm), field)) != NULL) \ 560 | TAILQ_NEXT((elm), field)->field.tqe_prev = \ 561 | (elm)->field.tqe_prev; \ 562 | else { \ 563 | (head)->tqh_last = (elm)->field.tqe_prev; \ 564 | QMD_TRACE_HEAD(head); \ 565 | } \ 566 | *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ 567 | TRASHIT((elm)->field.tqe_next); \ 568 | TRASHIT((elm)->field.tqe_prev); \ 569 | QMD_TRACE_ELEM(&(elm)->field); \ 570 | } while (0) 571 | 572 | 573 | #ifdef _KERNEL 574 | 575 | /* 576 | * XXX insque() and remque() are an old way of handling certain queues. 577 | * They bogusly assumes that all queue heads look alike. 578 | */ 579 | 580 | struct quehead { 581 | struct quehead *qh_link; 582 | struct quehead *qh_rlink; 583 | }; 584 | 585 | #ifdef __CC_SUPPORTS___INLINE 586 | 587 | static __inline void 588 | insque(void *a, void *b) 589 | { 590 | struct quehead *element = (struct quehead *)a, 591 | *head = (struct quehead *)b; 592 | 593 | element->qh_link = head->qh_link; 594 | element->qh_rlink = head; 595 | head->qh_link = element; 596 | element->qh_link->qh_rlink = element; 597 | } 598 | 599 | static __inline void 600 | remque(void *a) 601 | { 602 | struct quehead *element = (struct quehead *)a; 603 | 604 | element->qh_link->qh_rlink = element->qh_rlink; 605 | element->qh_rlink->qh_link = element->qh_link; 606 | element->qh_rlink = 0; 607 | } 608 | 609 | #else /* !__CC_SUPPORTS___INLINE */ 610 | 611 | void insque(void *a, void *b); 612 | void remque(void *a); 613 | 614 | #endif /* __CC_SUPPORTS___INLINE */ 615 | 616 | #endif /* _KERNEL */ 617 | 618 | #endif /* !_SYS_QUEUE_H_ */ 619 | 620 | -------------------------------------------------------------------------------- /src/pillowtalk_impl.c: -------------------------------------------------------------------------------- 1 | #include "pillowtalk_impl.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #ifdef PT_HAVE_YAJL_VERSION 12 | #include 13 | #endif 14 | #include 15 | 16 | #include "bsd_queue.h" 17 | 18 | /* YAJL Version checking */ 19 | #if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1) 20 | # define HAVE_YAJL_V2 1 21 | #endif 22 | 23 | /* Structs */ 24 | struct memory_chunk { 25 | char *memory; 26 | char *offset; 27 | size_t size; 28 | }; 29 | 30 | /* Prototypes */ 31 | static pt_response_t* http_operation(const char* method,const char* server_target, const char* data, unsigned data_len); 32 | static void *myrealloc(void *ptr, size_t size); 33 | static size_t recv_memory_callback(void *ptr, size_t size, size_t nmemb, void *data); 34 | static size_t send_memory_callback(void *ptr, size_t size, size_t nmemb, void *data); 35 | static int json_null(void* ctx); 36 | static int json_boolean(void* ctx,int boolean); 37 | #ifdef HAVE_YAJL_V2 38 | static int json_map_key(void * ctx, const unsigned char* str, size_t length); 39 | static int json_string(void* ctx, const unsigned char* str, size_t length); 40 | static int json_integer(void* ctx,long long integer); 41 | #else 42 | static int json_map_key(void * ctx, const unsigned char* str, unsigned int length); 43 | static int json_string(void* ctx, const unsigned char* str, unsigned int length); 44 | static int json_integer(void* ctx,long integer); 45 | #endif 46 | static int json_double(void* ctx,double dbl); 47 | static int json_start_map(void* ctx); 48 | static int json_end_map(void* ctx); 49 | static int json_start_array(void* ctx); 50 | static int json_end_array(void* ctx); 51 | static void generate_map_json(pt_map_t* map, yajl_gen g); 52 | static void generate_array_json(pt_array_t* map , yajl_gen g); 53 | static void generate_node_json(pt_node_t* node, yajl_gen g); 54 | static void free_map_node(pt_map_t* map); 55 | static void add_node_to_context_container(pt_parser_ctx_t* context, pt_node_t* value); 56 | static pt_node_t* parse_json(const char* json, int json_len); 57 | 58 | /* Globals */ 59 | static yajl_callbacks callbacks = { 60 | json_null, // null 61 | json_boolean, // boolean 62 | json_integer, // integer 63 | json_double, // double 64 | NULL, // number_string 65 | json_string, // string 66 | json_start_map, // start map 67 | json_map_key, // MAP KEY 68 | json_end_map, // end map 69 | json_start_array, // start array 70 | json_end_array, // end array 71 | }; 72 | 73 | 74 | /* Public Implementation */ 75 | 76 | void pt_init() 77 | { 78 | curl_global_init(CURL_GLOBAL_ALL); 79 | } 80 | 81 | void pt_cleanup() 82 | { 83 | /* we're done with libcurl, so clean it up */ 84 | curl_global_cleanup(); 85 | } 86 | 87 | void pt_free_response(pt_response_t* response) 88 | { 89 | if (response) { 90 | if (response->root) { 91 | pt_free_node(response->root); 92 | } 93 | free(response->raw_json); 94 | free(response); 95 | } 96 | } 97 | 98 | void free_parser_ctx(pt_parser_ctx_t* parser_ctx) 99 | { 100 | while(parser_ctx->stack) { 101 | pt_container_ctx_t* old_head = parser_ctx->stack; 102 | LL_DELETE(parser_ctx->stack,old_head); 103 | free(old_head); 104 | } 105 | free(parser_ctx); 106 | } 107 | 108 | pt_response_t* pt_delete(const char* server_target) 109 | { 110 | pt_response_t* res = http_operation("DELETE",server_target,NULL,0); 111 | res->root = parse_json(res->raw_json,res->raw_json_len); 112 | return res; 113 | } 114 | 115 | pt_response_t* pt_put(const char* server_target, pt_node_t* doc) 116 | { 117 | char* data = NULL; 118 | int data_len = 0; 119 | if (doc) { 120 | data = pt_to_json(doc,0); 121 | if (data) 122 | data_len = strlen(data); 123 | } 124 | pt_response_t* res = http_operation("PUT",server_target,data,data_len); 125 | res->root = parse_json(res->raw_json,res->raw_json_len); 126 | if (data) 127 | free(data); 128 | return res; 129 | } 130 | 131 | pt_response_t* pt_put_raw(const char* server_target, const char* data, unsigned int data_len) 132 | { 133 | pt_response_t* res = http_operation("PUT",server_target,data,data_len); 134 | res->root = parse_json(res->raw_json,res->raw_json_len); 135 | return res; 136 | } 137 | 138 | pt_response_t* pt_unparsed_get(const char* server_target) 139 | { 140 | pt_response_t* res = http_operation("GET",server_target,NULL,0); 141 | return res; 142 | } 143 | 144 | pt_response_t* pt_get(const char* server_target) 145 | { 146 | pt_response_t* res = http_operation("GET",server_target,NULL,0); 147 | res->root = parse_json(res->raw_json,res->raw_json_len); 148 | return res; 149 | } 150 | 151 | pt_node_t* pt_map_get(pt_node_t* map,const char* key) 152 | { 153 | if (map && map->type == PT_MAP && key) { 154 | pt_map_t* real_map = (pt_map_t*) map; 155 | pt_key_value_t* search_result = NULL; 156 | HASH_FIND(hh,real_map->key_values,key,strlen(key),search_result); 157 | if (search_result) { 158 | return search_result->value; 159 | } else { 160 | return NULL; 161 | } 162 | } else { 163 | return NULL; 164 | } 165 | } 166 | 167 | unsigned int pt_array_len(pt_node_t* array) 168 | { 169 | if (array && array->type == PT_ARRAY) { 170 | return ((pt_array_t*) array)->len; 171 | } else { 172 | return 0; 173 | } 174 | } 175 | 176 | pt_node_t* pt_array_get(pt_node_t* array, unsigned int idx) 177 | { 178 | if (array && array->type == PT_ARRAY) { 179 | pt_array_t* real_array = (pt_array_t*) array; 180 | int index = 0; 181 | pt_array_elem_t* cur = NULL; 182 | TAILQ_FOREACH(cur,&real_array->head, entries) { 183 | if (index == idx) { 184 | return cur->node; 185 | } 186 | index++; 187 | } 188 | } 189 | return NULL; 190 | } 191 | 192 | /* Pass in the pointer to the elem and remove it if it exists */ 193 | void pt_array_remove(pt_node_t* array, pt_node_t* node) 194 | { 195 | if (array && array->type == PT_ARRAY) { 196 | pt_array_t* real_array = (pt_array_t*) array; 197 | pt_array_elem_t* cur = NULL; 198 | pt_array_elem_t* tmp = NULL; 199 | TAILQ_FOREACH_SAFE(cur,&real_array->head, entries, tmp) { 200 | if (cur->node == node) { 201 | TAILQ_REMOVE(&real_array->head,cur,entries); 202 | pt_free_node(cur->node); 203 | free(cur); 204 | real_array->len--; 205 | break; 206 | } 207 | } 208 | } 209 | } 210 | 211 | void pt_array_push_front(pt_node_t* array, pt_node_t* node) 212 | { 213 | if (array && array->type == PT_ARRAY) { 214 | pt_array_t* real_array = (pt_array_t*) array; 215 | pt_array_elem_t* elem = (pt_array_elem_t*) malloc(sizeof(pt_array_elem_t)); 216 | elem->node = node; 217 | real_array->len++; 218 | TAILQ_INSERT_HEAD(&real_array->head,elem,entries); 219 | } 220 | } 221 | 222 | void pt_array_push_back(pt_node_t* array, pt_node_t* node) 223 | { 224 | if (array && array->type == PT_ARRAY) { 225 | pt_array_t* real_array = (pt_array_t*) array; 226 | pt_array_elem_t* elem = (pt_array_elem_t*) malloc(sizeof(pt_array_elem_t)); 227 | elem->node = node; 228 | real_array->len++; 229 | TAILQ_INSERT_TAIL(&real_array->head,elem,entries); 230 | } 231 | } 232 | 233 | pt_iterator_t* pt_iterator(pt_node_t* node) 234 | { 235 | if (node) { 236 | if (node->type == PT_ARRAY) { 237 | pt_array_t* real_array = (pt_array_t*) node; 238 | pt_iterator_impl_t* iter = (pt_iterator_impl_t*) calloc(1,sizeof(pt_iterator_impl_t)); 239 | iter->type = PT_ARRAY_ITERATOR; 240 | iter->next_array_elem = TAILQ_FIRST(&real_array->head); 241 | return (pt_iterator_t*) iter; 242 | } else if (node->type == PT_MAP) { 243 | pt_map_t* real_map = (pt_map_t*) node; 244 | pt_iterator_impl_t* iter = (pt_iterator_impl_t*) calloc(1,sizeof(pt_iterator_impl_t)); 245 | iter->type = PT_MAP_ITERATOR; 246 | iter->next_map_pair = real_map->key_values; 247 | return (pt_iterator_t*) iter; 248 | } 249 | } 250 | return NULL; 251 | } 252 | 253 | pt_node_t* pt_iterator_next(pt_iterator_t* iter, const char** key) 254 | { 255 | if (iter) { 256 | pt_iterator_impl_t* real_iter = (pt_iterator_impl_t*) iter; 257 | if (real_iter->type == PT_MAP_ITERATOR) { 258 | pt_key_value_t* kv = real_iter->next_map_pair; 259 | if (kv) { 260 | pt_node_t* ret = kv->value; 261 | if (key) 262 | *key = kv->key; 263 | real_iter->next_map_pair = kv->hh.next; 264 | return ret; 265 | } 266 | } else if (real_iter->type == PT_ARRAY_ITERATOR) { 267 | pt_array_elem_t* elem = real_iter->next_array_elem; 268 | if (elem) { 269 | pt_node_t* ret = elem->node; 270 | real_iter->next_array_elem = TAILQ_NEXT(elem,entries); 271 | return ret; 272 | } 273 | } 274 | } 275 | return NULL; 276 | } 277 | 278 | int pt_is_null(pt_node_t* null) 279 | { 280 | return !null || null->type == PT_NULL; 281 | } 282 | 283 | int pt_boolean_get(pt_node_t* boolean) 284 | { 285 | if (boolean && boolean->type == PT_BOOLEAN) { 286 | pt_bool_value_t* bool_node = (pt_bool_value_t*) boolean; 287 | return bool_node->value; 288 | } else { 289 | return 0; 290 | } 291 | } 292 | 293 | int pt_integer_get(pt_node_t* integer) 294 | { 295 | if (integer && integer->type == PT_INTEGER) { 296 | return ((pt_int_value_t*) integer)->value; 297 | } else if (integer && integer->type == PT_DOUBLE) { 298 | return (int) ((pt_double_value_t*) integer)->value; 299 | } else { 300 | return 0; 301 | } 302 | } 303 | 304 | double pt_double_get(pt_node_t* dbl) 305 | { 306 | if (dbl && dbl->type == PT_DOUBLE) { 307 | return ((pt_double_value_t*) dbl)->value; 308 | } else if (dbl && dbl->type == PT_INTEGER) { 309 | return (double) ((pt_int_value_t*) dbl)->value; 310 | } else { 311 | return 0; 312 | } 313 | } 314 | 315 | const char* pt_string_get(pt_node_t* string) 316 | { 317 | if (string && string->type == PT_STRING) { 318 | return ((pt_str_value_t*) string)->value; 319 | } else { 320 | return NULL; 321 | } 322 | } 323 | 324 | /* Build a new pt_map_t* and initialize it */ 325 | pt_node_t* pt_map_new() 326 | { 327 | pt_node_t* new_node = (pt_node_t*) calloc(1,sizeof(pt_map_t)); 328 | new_node->type = PT_MAP; 329 | return new_node; 330 | } 331 | 332 | pt_node_t* pt_null_new() 333 | { 334 | pt_node_t* new_node = (pt_node_t*) calloc(1,sizeof(pt_null_value_t)); 335 | new_node->type = PT_NULL; 336 | return new_node; 337 | } 338 | 339 | pt_node_t* pt_bool_new(int boolean) 340 | { 341 | pt_bool_value_t* new_node = (pt_bool_value_t*) calloc(1,sizeof(pt_bool_value_t)); 342 | new_node->parent.type = PT_BOOLEAN; 343 | new_node->value = boolean; 344 | return (pt_node_t*) new_node; 345 | } 346 | 347 | pt_node_t* pt_integer_new(int integer) 348 | { 349 | pt_int_value_t* new_node = (pt_int_value_t*) calloc(1,sizeof(pt_int_value_t)); 350 | new_node->parent.type = PT_INTEGER; 351 | new_node->value = integer; 352 | return (pt_node_t*) new_node; 353 | } 354 | 355 | pt_node_t* pt_double_new(double dbl) 356 | { 357 | pt_double_value_t* new_node = (pt_double_value_t*) calloc(1,sizeof(pt_double_value_t)); 358 | new_node->parent.type = PT_DOUBLE; 359 | new_node->value = dbl; 360 | return (pt_node_t*) new_node; 361 | } 362 | 363 | void pt_map_set(pt_node_t* map, const char* key, pt_node_t* value) 364 | { 365 | if (map && map->type == PT_MAP && key && value) { 366 | pt_map_t* real_map = (pt_map_t*) map; 367 | pt_key_value_t* search_result = NULL; 368 | HASH_FIND(hh,real_map->key_values,key,strlen(key),search_result); 369 | if (search_result) { 370 | // free the old value 371 | pt_free_node(search_result->value); 372 | search_result->value = value; 373 | } else { 374 | pt_key_value_t* new_node = (pt_key_value_t*) calloc(1,sizeof(pt_key_value_t)); 375 | char* new_key = strdup(key); 376 | new_node->parent.type = PT_KEY_VALUE; 377 | new_node->key = new_key; 378 | new_node->value = value; 379 | HASH_ADD_KEYPTR(hh,real_map->key_values,new_node->key,strlen(new_node->key),new_node); 380 | } 381 | } 382 | } 383 | 384 | void pt_map_unset(pt_node_t* map, const char* key) 385 | { 386 | if (map && map->type == PT_MAP) { 387 | pt_map_t* real_map = (pt_map_t*) map; 388 | pt_key_value_t* search_result = NULL; 389 | HASH_FIND(hh,real_map->key_values,key,strlen(key),search_result); 390 | if (search_result) { 391 | HASH_DEL(real_map->key_values,search_result); 392 | pt_free_node(search_result->value); 393 | free(search_result->key); 394 | free(search_result); 395 | } 396 | } 397 | } 398 | 399 | pt_node_t* pt_string_new(const char* str) 400 | { 401 | pt_str_value_t* new_node = (pt_str_value_t*) calloc(1,sizeof(pt_str_value_t)); 402 | new_node->parent.type = PT_STRING; 403 | new_node->value = strdup(str); 404 | return (pt_node_t*) new_node; 405 | } 406 | 407 | pt_node_t* pt_array_new() 408 | { 409 | pt_node_t* new_node = (pt_node_t*) calloc(1,sizeof(pt_array_t)); 410 | new_node->type = PT_ARRAY; 411 | TAILQ_INIT(&((pt_array_t*) new_node)->head); 412 | return new_node; 413 | } 414 | 415 | char* pt_to_json(pt_node_t* root, int beautify) 416 | { 417 | #ifdef HAVE_YAJL_V2 418 | yajl_gen g = yajl_gen_alloc(NULL); 419 | yajl_gen_config(g, yajl_gen_beautify, beautify); 420 | yajl_gen_config(g, yajl_gen_indent_string, " "); 421 | #else 422 | yajl_gen_config conf = { beautify," "}; 423 | yajl_gen g = yajl_gen_alloc(&conf, NULL); 424 | #endif 425 | 426 | generate_node_json(root,g); 427 | 428 | const unsigned char * gen_buf = NULL; 429 | char* json = NULL; 430 | #ifdef HAVE_YAJL_V2 431 | size_t len = 0; 432 | #else 433 | unsigned int len = 0; 434 | #endif 435 | 436 | yajl_gen_get_buf(g, &gen_buf, &len); 437 | 438 | json = (char*) malloc(len + 1); 439 | memcpy(json,gen_buf,len); 440 | json[len] = '\0'; 441 | 442 | yajl_gen_free(g); 443 | return json; 444 | } 445 | 446 | pt_node_t* pt_from_json(const char* json) 447 | { 448 | pt_node_t* root = parse_json(json,strlen(json)); 449 | return root; 450 | } 451 | 452 | int pt_map_update(pt_node_t* root, pt_node_t* additions, int append) 453 | { 454 | if (!root || !additions || root->type != PT_MAP || additions->type != PT_MAP) 455 | return 1; 456 | 457 | //pt_map_t* root_map = (pt_map_t*) root; 458 | pt_map_t* additions_map = (pt_map_t*) additions; 459 | pt_key_value_t* key_value = NULL; 460 | for(key_value = additions_map->key_values; key_value != NULL; key_value = key_value->hh.next) { 461 | if (key_value->value) { 462 | pt_node_t* existing = pt_map_get(root,key_value->key); 463 | if (!existing) { 464 | pt_map_set(root,key_value->key,pt_clone(key_value->value)); 465 | } else { 466 | if (key_value->value->type != existing->type) { 467 | return 1; 468 | } 469 | switch(key_value->value->type) { 470 | case PT_MAP: 471 | pt_map_update(existing,key_value->value,append); 472 | break; 473 | default: 474 | pt_map_set(root,key_value->key,pt_clone(key_value->value)); 475 | break; 476 | } 477 | } 478 | } 479 | } 480 | return 0; 481 | } 482 | 483 | pt_node_t* pt_clone(pt_node_t* root) 484 | { 485 | if (root) { 486 | switch(root->type) { 487 | case PT_MAP: 488 | { 489 | pt_node_t* clone = pt_map_new(); 490 | pt_map_t* map = (pt_map_t*) root; 491 | pt_key_value_t* key_value = NULL; 492 | for(key_value = map->key_values; key_value != NULL; key_value = key_value->hh.next) { 493 | pt_map_set(clone,key_value->key,pt_clone(key_value->value)); 494 | } 495 | return (pt_node_t*) clone; 496 | } 497 | case PT_ARRAY: 498 | { 499 | pt_node_t* clone = pt_array_new(); 500 | pt_array_t* array = (pt_array_t*) root; 501 | pt_array_elem_t* elem = TAILQ_FIRST(&array->head); 502 | while(elem) { 503 | pt_array_push_back(clone,pt_clone(elem->node)); 504 | elem = TAILQ_NEXT(elem,entries); 505 | } 506 | return clone; 507 | } 508 | case PT_NULL: 509 | return pt_null_new(); 510 | case PT_BOOLEAN: 511 | return pt_bool_new(((pt_bool_value_t*) root)->value); 512 | case PT_INTEGER: 513 | return pt_integer_new(((pt_int_value_t*) root)->value); 514 | case PT_DOUBLE: 515 | return pt_double_new(((pt_double_value_t*) root)->value); 516 | case PT_STRING: 517 | return pt_string_new(((pt_str_value_t*) root)->value); 518 | case PT_KEY_VALUE: 519 | break; 520 | } 521 | } 522 | return NULL; 523 | } 524 | 525 | 526 | /* Static Implementation */ 527 | 528 | /* 529 | * This method wraps basic curl functionality 530 | */ 531 | static pt_response_t* http_operation(const char* http_method, const char* server_target, const char* data, unsigned data_len) 532 | { 533 | CURL *curl_handle; 534 | CURLcode ret; 535 | struct memory_chunk recv_chunk; 536 | recv_chunk.memory=NULL; /* we expect realloc(NULL, size) to work */ 537 | recv_chunk.size = 0; /* no data at this point */ 538 | 539 | struct memory_chunk send_chunk = {0,0,0}; 540 | 541 | /* init the curl session */ 542 | curl_handle = curl_easy_init(); 543 | 544 | /* specify URL to get */ 545 | curl_easy_setopt(curl_handle, CURLOPT_URL, server_target); 546 | 547 | curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 10); 548 | 549 | // Want to avoid CURL SIGNALS 550 | curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); 551 | 552 | printf("%s : %s\n",http_method,server_target); 553 | 554 | if (!strcmp("PUT",http_method)) 555 | curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1); 556 | else 557 | curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, http_method); 558 | 559 | if (data && data_len > 0) { 560 | send_chunk.memory = (char*) malloc(data_len); 561 | memcpy(send_chunk.memory,data,data_len); 562 | send_chunk.offset = send_chunk.memory; 563 | send_chunk.size = data_len; 564 | curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, send_memory_callback); 565 | curl_easy_setopt(curl_handle, CURLOPT_READDATA, (void*) &send_chunk); 566 | } 567 | 568 | /* send all data to this function */ 569 | curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, recv_memory_callback); 570 | 571 | /* we pass our 'chunk' struct to the callback function */ 572 | curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&recv_chunk); 573 | 574 | /* some servers don't like requests that are made without a user-agent 575 | field, so we provide one */ 576 | curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "pillowtalk-agent/0.1"); 577 | 578 | /* get it! */ 579 | ret = curl_easy_perform(curl_handle); 580 | 581 | pt_response_t* res = calloc(1,sizeof(pt_response_t)); 582 | if ((!ret)) { 583 | ret = curl_easy_getinfo(curl_handle,CURLINFO_RESPONSE_CODE, &res->response_code); 584 | if (ret != CURLE_OK) 585 | res->response_code = 500; 586 | 587 | if (recv_chunk.size > 0) { 588 | // Parse the JSON chunk returned 589 | recv_chunk.memory[recv_chunk.size] = '\0'; 590 | res->raw_json = recv_chunk.memory; 591 | res->raw_json_len = recv_chunk.size; 592 | } 593 | } else { 594 | res->response_code = 500; 595 | } 596 | 597 | if (send_chunk.memory) 598 | free(send_chunk.memory); 599 | 600 | /* cleanup curl stuff */ 601 | curl_easy_cleanup(curl_handle); 602 | return res; 603 | } 604 | 605 | static void *myrealloc(void *ptr, size_t size) 606 | { 607 | /* There might be a realloc() out there that doesn't like reallocing 608 | NULL pointers, so we take care of it here */ 609 | if(ptr) 610 | return (void*) realloc(ptr, size); 611 | else 612 | return (void*) malloc(size); 613 | } 614 | 615 | static size_t recv_memory_callback(void *ptr, size_t size, size_t nmemb, void *data) 616 | { 617 | size_t realsize = size * nmemb; 618 | struct memory_chunk *mem = (struct memory_chunk*)data; 619 | 620 | mem->memory = (char*) myrealloc(mem->memory, mem->size + realsize + 1); 621 | if (mem->memory) { 622 | memcpy(&(mem->memory[mem->size]), ptr, realsize); 623 | mem->size += realsize; 624 | mem->memory[mem->size] = 0; 625 | } 626 | return realsize; 627 | } 628 | 629 | static size_t send_memory_callback(void *ptr, size_t size, size_t nmemb, void *data) 630 | { 631 | size_t realsize = size * nmemb; 632 | if(realsize < 1) 633 | return 0; 634 | 635 | struct memory_chunk* mem = (struct memory_chunk*) data; 636 | if (mem->size > 0) { 637 | size_t bytes_to_copy = (mem->size > realsize) ? realsize : mem->size; 638 | memcpy(ptr,mem->offset,bytes_to_copy); 639 | mem->offset += bytes_to_copy; 640 | mem->size -= bytes_to_copy; 641 | return bytes_to_copy; 642 | } 643 | return 0; 644 | } 645 | 646 | /* Yajl Callbacks */ 647 | static int json_null(void* ctx) 648 | { 649 | pt_node_t* node = (pt_node_t*) malloc(sizeof(pt_node_t)); 650 | node->type = PT_NULL; 651 | add_node_to_context_container((pt_parser_ctx_t*) ctx,node); 652 | return 1; 653 | } 654 | 655 | static int json_boolean(void* ctx,int boolean) 656 | { 657 | pt_bool_value_t * node = (pt_bool_value_t*) calloc(1,sizeof(pt_bool_value_t)); 658 | node->parent.type = PT_BOOLEAN; 659 | node->value = boolean; 660 | add_node_to_context_container((pt_parser_ctx_t*) ctx,(pt_node_t*)node); 661 | return 1; 662 | } 663 | 664 | #ifdef HAVE_YAJL_V2 665 | static int json_map_key(void* ctx, const unsigned char* str, size_t length) 666 | #else 667 | static int json_map_key(void* ctx, const unsigned char* str, unsigned int length) 668 | #endif 669 | { 670 | pt_parser_ctx_t* parser_ctx= (pt_parser_ctx_t*) ctx; 671 | assert(parser_ctx->stack && parser_ctx->stack->container->type == PT_MAP); 672 | pt_map_t* container = (pt_map_t*) parser_ctx->stack->container; 673 | pt_key_value_t* new_node = (pt_key_value_t*) calloc(1,sizeof(pt_key_value_t)); 674 | char* new_str = (char*) malloc(length + 1); 675 | memcpy(new_str,str,length); 676 | new_str[length] = 0x0; 677 | new_node->key = new_str; 678 | new_node->parent.type = PT_KEY_VALUE; 679 | HASH_ADD_KEYPTR(hh,container->key_values,new_node->key,length,new_node); 680 | parser_ctx->stack->cur = (pt_node_t*) new_node; 681 | return 1; 682 | } 683 | 684 | #ifdef HAVE_YAJL_V2 685 | static int json_integer(void* ctx,long long integer) 686 | #else 687 | static int json_integer(void* ctx,long integer) 688 | #endif 689 | { 690 | pt_int_value_t* node = (pt_int_value_t*) calloc(1,sizeof(pt_int_value_t)); 691 | node->parent.type = PT_INTEGER; 692 | node->value = integer; 693 | 694 | add_node_to_context_container(ctx,(pt_node_t*) node); 695 | return 1; 696 | } 697 | 698 | static int json_double(void* ctx,double dbl) 699 | { 700 | pt_double_value_t* node = (pt_double_value_t*) calloc(1,sizeof(pt_double_value_t)); 701 | node->parent.type = PT_DOUBLE; 702 | node->value = dbl; 703 | 704 | add_node_to_context_container(ctx,(pt_node_t*) node); 705 | return 1; 706 | } 707 | 708 | #ifdef HAVE_YAJL_V2 709 | static int json_string(void* ctx, const unsigned char* str, size_t length) 710 | #else 711 | static int json_string(void* ctx, const unsigned char* str, unsigned int length) 712 | #endif 713 | { 714 | char* new_str = (char*) malloc(length + 1); 715 | memcpy(new_str,str,length); 716 | new_str[length] = 0x0; 717 | pt_str_value_t* node = (pt_str_value_t*) calloc(1,sizeof(pt_str_value_t)); 718 | node->parent.type = PT_STRING; 719 | node->value = new_str; 720 | add_node_to_context_container(ctx,(pt_node_t*) node); 721 | return 1; 722 | } 723 | 724 | /* If we aren't in a key value pair then we create a new node, otherwise we are 725 | * the value 726 | */ 727 | static int json_start_map(void* ctx) 728 | { 729 | pt_parser_ctx_t* parser_ctx = (pt_parser_ctx_t*) ctx; 730 | pt_node_t* new_node = (pt_node_t*) calloc(1,sizeof(pt_map_t)); 731 | new_node->type = PT_MAP; 732 | add_node_to_context_container(parser_ctx,new_node); 733 | pt_container_ctx_t* new_ctx = (pt_container_ctx_t*) calloc(1,sizeof(pt_container_ctx_t)); 734 | new_ctx->container = new_node; 735 | LL_PREPEND(parser_ctx->stack,new_ctx); 736 | return 1; 737 | } 738 | 739 | static int json_end_map(void* ctx) 740 | { 741 | pt_parser_ctx_t* parser_ctx = (pt_parser_ctx_t*) ctx; 742 | assert(parser_ctx->stack->container->type == PT_MAP); 743 | if (parser_ctx->stack) { 744 | pt_container_ctx_t* old_head = parser_ctx->stack; 745 | LL_DELETE(parser_ctx->stack,old_head); 746 | free(old_head); 747 | } 748 | return 1; 749 | } 750 | 751 | static int json_start_array(void* ctx) 752 | { 753 | pt_parser_ctx_t* parser_ctx = (pt_parser_ctx_t*) ctx; 754 | pt_array_t* new_node = (pt_array_t*) calloc(1,sizeof(pt_array_t)); 755 | TAILQ_INIT(&new_node->head); 756 | new_node->parent.type = PT_ARRAY; 757 | add_node_to_context_container(parser_ctx,(pt_node_t*) new_node); 758 | pt_container_ctx_t* new_ctx = (pt_container_ctx_t*) calloc(1,sizeof(pt_container_ctx_t)); 759 | new_ctx->container = (pt_node_t*) new_node; 760 | new_ctx->cur = (pt_node_t*) new_node; 761 | LL_PREPEND(parser_ctx->stack,new_ctx); 762 | return 1; 763 | } 764 | 765 | static int json_end_array(void* ctx) 766 | { 767 | pt_parser_ctx_t* parser_ctx = (pt_parser_ctx_t*) ctx; 768 | assert(parser_ctx->stack->container->type == PT_ARRAY); 769 | if (parser_ctx->stack) { 770 | pt_container_ctx_t* old_head = parser_ctx->stack; 771 | LL_DELETE(parser_ctx->stack,old_head); 772 | free(old_head); 773 | } 774 | return 1; 775 | } 776 | 777 | /* 778 | * This function looks to see what the current node and adds the new value node to it. 779 | * If it is an array it appends the value to the array. 780 | * If it is a key value pair it adds it to the value field of that pair. 781 | */ 782 | static void add_node_to_context_container(pt_parser_ctx_t* context, pt_node_t* value) 783 | { 784 | if (context->stack && context->stack->cur) { 785 | pt_node_t* cur = context->stack->cur; 786 | if (cur->type == PT_ARRAY) { 787 | pt_array_t* resolved = (pt_array_t*) cur; 788 | pt_array_elem_t* elem = (pt_array_elem_t*) malloc(sizeof(pt_array_elem_t)); 789 | elem->node = value; 790 | TAILQ_INSERT_TAIL(&resolved->head,elem,entries); 791 | resolved->len++; 792 | } else if (cur->type == PT_KEY_VALUE) { 793 | pt_key_value_t* resolved = (pt_key_value_t*) cur; 794 | resolved->value = value; 795 | } else { 796 | printf("Shouldn't get here: %d:%d\n", cur->type, value->type); 797 | } 798 | } else { 799 | context->root = value; 800 | } 801 | } 802 | 803 | static pt_node_t* parse_json(const char* json, int json_len) 804 | { 805 | yajl_status stat; 806 | yajl_handle hand; 807 | #ifndef HAVE_YAJL_V2 808 | yajl_parser_config cfg = { 0, 1 }; 809 | #endif 810 | 811 | pt_parser_ctx_t* parser_ctx = (pt_parser_ctx_t*) calloc(1,sizeof(pt_parser_ctx_t)); 812 | 813 | #ifdef HAVE_YAJL_V2 814 | hand = yajl_alloc(&callbacks, NULL, parser_ctx); 815 | // don't allow comments 816 | yajl_config(hand, yajl_allow_comments, 0); 817 | // DO validate strings 818 | yajl_config(hand, yajl_dont_validate_strings, 0); 819 | #else 820 | hand = yajl_alloc(&callbacks, &cfg, NULL, parser_ctx); 821 | #endif 822 | 823 | stat = yajl_parse(hand, (const unsigned char*) json, json_len); 824 | #ifdef HAVE_YAJL_V2 825 | if (stat != yajl_status_ok && stat != yajl_status_client_canceled) { 826 | #else 827 | if (stat != yajl_status_ok && stat != yajl_status_insufficient_data) { 828 | #endif 829 | unsigned char * str = yajl_get_error(hand, 1, (const unsigned char*) json, json_len); 830 | fprintf(stderr, "%s",(const char *) str); 831 | yajl_free_error(hand, str); 832 | } 833 | 834 | pt_node_t* root = parser_ctx->root; 835 | free_parser_ctx(parser_ctx); 836 | yajl_free(hand); 837 | return root; 838 | } 839 | 840 | static void free_map_node(pt_map_t* map) 841 | { 842 | pt_key_value_t* cur = NULL; 843 | while(map->key_values) { 844 | cur = map->key_values; 845 | HASH_DEL(map->key_values, cur); 846 | pt_free_node(cur->value); 847 | free(cur->key); 848 | free(cur); 849 | } 850 | } 851 | 852 | static void free_array_node(pt_array_t* array) 853 | { 854 | while (!TAILQ_EMPTY(&array->head)) { 855 | pt_array_elem_t* elem; 856 | elem = TAILQ_FIRST(&array->head); 857 | TAILQ_REMOVE(&array->head, elem, entries); 858 | pt_free_node(elem->node); 859 | free(elem); 860 | } 861 | } 862 | 863 | /* Recursive Free Function. Watch the fireworks! */ 864 | void pt_free_node(pt_node_t* node) 865 | { 866 | if (node) { 867 | switch(node->type) { 868 | case PT_MAP: 869 | { 870 | free_map_node((pt_map_t*) node); 871 | } 872 | break; 873 | case PT_ARRAY: 874 | { 875 | free_array_node((pt_array_t*) node); 876 | } 877 | break; 878 | case PT_STRING: 879 | free(((pt_str_value_t*) node)->value); 880 | break; 881 | default: 882 | break; 883 | // the basic value types will get handled in the free(node) below 884 | } 885 | free(node); 886 | } 887 | } 888 | 889 | static void generate_map_json(pt_map_t* map, yajl_gen g) 890 | { 891 | pt_key_value_t* cur = NULL; 892 | 893 | yajl_gen_map_open(g); 894 | for(cur=map->key_values; cur != NULL; cur=cur->hh.next) { 895 | yajl_gen_string(g,(const unsigned char*) cur->key,strlen(cur->key)); 896 | generate_node_json(cur->value,g); 897 | } 898 | yajl_gen_map_close(g); 899 | } 900 | 901 | static void generate_array_json(pt_array_t* array, yajl_gen g) 902 | { 903 | pt_array_t* real_array = (pt_array_t*) array; 904 | pt_array_elem_t* cur = NULL; 905 | yajl_gen_array_open(g); 906 | TAILQ_FOREACH(cur,&real_array->head, entries) { 907 | generate_node_json(cur->node,g); 908 | } 909 | yajl_gen_array_close(g); 910 | } 911 | 912 | static void generate_node_json(pt_node_t* node, yajl_gen g) 913 | { 914 | if (node) { 915 | switch(node->type) { 916 | case PT_ARRAY: 917 | generate_array_json((pt_array_t*) node,g); 918 | break; 919 | 920 | case PT_MAP: 921 | generate_map_json((pt_map_t*) node,g); 922 | break; 923 | 924 | case PT_NULL: 925 | yajl_gen_null(g); 926 | break; 927 | 928 | case PT_BOOLEAN: 929 | yajl_gen_bool(g,((pt_bool_value_t*) node)->value); 930 | break; 931 | 932 | case PT_INTEGER: 933 | yajl_gen_integer(g,((pt_int_value_t*) node)->value); 934 | break; 935 | 936 | case PT_DOUBLE: 937 | yajl_gen_double(g,((pt_double_value_t*) node)->value); 938 | break; 939 | 940 | case PT_STRING: 941 | yajl_gen_string(g,(const unsigned char*) ((pt_str_value_t*) node)->value, strlen(((pt_str_value_t*) node)->value)); 942 | break; 943 | 944 | case PT_KEY_VALUE: 945 | break; 946 | } 947 | } else { 948 | yajl_gen_null(g); 949 | } 950 | } 951 | -------------------------------------------------------------------------------- /src/uthash.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2009, Troy D. Hanson http://uthash.sourceforge.net 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef UTHASH_H 25 | #define UTHASH_H 26 | 27 | #include /* memcmp,strlen */ 28 | #include /* ptrdiff_t */ 29 | #include /* uint32_t etc */ 30 | 31 | #define UTHASH_VERSION 1.6 32 | 33 | /* C++ requires extra stringent casting */ 34 | #if defined __cplusplus 35 | #define TYPEOF(x) (typeof(x)) 36 | #else 37 | #define TYPEOF(x) 38 | #endif 39 | 40 | 41 | #define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ 42 | #define uthash_bkt_malloc(sz) malloc(sz) /* malloc fcn for UT_hash_bucket's */ 43 | #define uthash_bkt_free(ptr) free(ptr) /* free fcn for UT_hash_bucket's */ 44 | #define uthash_tbl_malloc(sz) malloc(sz) /* malloc fcn for UT_hash_table */ 45 | #define uthash_tbl_free(ptr) free(ptr) /* free fcn for UT_hash_table */ 46 | 47 | #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ 48 | #define uthash_expand_fyi(tbl) /* can be defined to log expands */ 49 | 50 | /* initial number of buckets */ 51 | #define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ 52 | #define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ 53 | #define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ 54 | 55 | /* calculate the element whose hash handle address is hhe */ 56 | #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)hhp) - (tbl)->hho)) 57 | 58 | #define HASH_FIND(hh,head,keyptr,keylen,out) \ 59 | do { \ 60 | unsigned _hf_bkt,_hf_hashv; \ 61 | out=TYPEOF(out)head; \ 62 | if (head) { \ 63 | HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ 64 | HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ 65 | keyptr,keylen,out); \ 66 | } \ 67 | } while (0) 68 | 69 | #define HASH_MAKE_TABLE(hh,head) \ 70 | do { \ 71 | (head)->hh.tbl = (UT_hash_table*)uthash_tbl_malloc( \ 72 | sizeof(UT_hash_table)); \ 73 | if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ 74 | memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ 75 | (head)->hh.tbl->tail = &((head)->hh); \ 76 | (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ 77 | (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ 78 | (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ 79 | (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_bkt_malloc( \ 80 | HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ 81 | if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ 82 | memset((head)->hh.tbl->buckets, 0, \ 83 | HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ 84 | } while(0) 85 | 86 | #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ 87 | HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add) 88 | 89 | #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ 90 | do { \ 91 | unsigned _ha_bkt; \ 92 | (add)->hh.next = NULL; \ 93 | (add)->hh.key = (char*)keyptr; \ 94 | (add)->hh.keylen = keylen_in; \ 95 | if (!(head)) { \ 96 | head = (add); \ 97 | (head)->hh.prev = NULL; \ 98 | HASH_MAKE_TABLE(hh,head); \ 99 | } else { \ 100 | (head)->hh.tbl->tail->next = (add); \ 101 | (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ 102 | (head)->hh.tbl->tail = &((add)->hh); \ 103 | } \ 104 | (head)->hh.tbl->num_items++; \ 105 | (add)->hh.tbl = (head)->hh.tbl; \ 106 | HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ 107 | (add)->hh.hashv, _ha_bkt); \ 108 | HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ 109 | HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ 110 | HASH_FSCK(hh,head); \ 111 | } while(0) 112 | 113 | #define HASH_TO_BKT( hashv, num_bkts, bkt ) \ 114 | do { \ 115 | bkt = ((hashv) & ((num_bkts) - 1)); \ 116 | } while(0) 117 | 118 | /* delete "delptr" from the hash table. 119 | * "the usual" patch-up process for the app-order doubly-linked-list. 120 | * The use of _hd_hh_del below deserves special explanation. 121 | * These used to be expressed using (delptr) but that led to a bug 122 | * if someone used the same symbol for the head and deletee, like 123 | * HASH_DELETE(hh,users,users); 124 | * We want that to work, but by changing the head (users) below 125 | * we were forfeiting our ability to further refer to the deletee (users) 126 | * in the patch-up process. Solution: use scratch space in the table to 127 | * copy the deletee pointer, then the latter references are via that 128 | * scratch pointer rather than through the repointed (users) symbol. 129 | */ 130 | #define HASH_DELETE(hh,head,delptr) \ 131 | do { \ 132 | unsigned _hd_bkt; \ 133 | struct UT_hash_handle *_hd_hh_del; \ 134 | if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ 135 | uthash_bkt_free((head)->hh.tbl->buckets ); \ 136 | uthash_tbl_free((head)->hh.tbl); \ 137 | head = NULL; \ 138 | } else { \ 139 | _hd_hh_del = &((delptr)->hh); \ 140 | if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ 141 | (head)->hh.tbl->tail = \ 142 | (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ 143 | (head)->hh.tbl->hho); \ 144 | } \ 145 | if ((delptr)->hh.prev) { \ 146 | ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ 147 | (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ 148 | } else { \ 149 | head = TYPEOF(head)((delptr)->hh.next); \ 150 | } \ 151 | if (_hd_hh_del->next) { \ 152 | ((UT_hash_handle*)((char*)_hd_hh_del->next + \ 153 | (head)->hh.tbl->hho))->prev = \ 154 | _hd_hh_del->prev; \ 155 | } \ 156 | HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ 157 | HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ 158 | (head)->hh.tbl->num_items--; \ 159 | } \ 160 | HASH_FSCK(hh,head); \ 161 | } while (0) 162 | 163 | 164 | /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ 165 | #define HASH_FIND_STR(head,findstr,out) \ 166 | HASH_FIND(hh,head,findstr,strlen(findstr),out) 167 | #define HASH_ADD_STR(head,strfield,add) \ 168 | HASH_ADD(hh,head,strfield,strlen(add->strfield),add) 169 | #define HASH_FIND_INT(head,findint,out) \ 170 | HASH_FIND(hh,head,findint,sizeof(int),out) 171 | #define HASH_ADD_INT(head,intfield,add) \ 172 | HASH_ADD(hh,head,intfield,sizeof(int),add) 173 | #define HASH_DEL(head,delptr) \ 174 | HASH_DELETE(hh,head,delptr) 175 | 176 | /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. 177 | * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. 178 | */ 179 | #ifdef HASH_DEBUG 180 | #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) 181 | #define HASH_FSCK(hh,head) \ 182 | do { \ 183 | unsigned _bkt_i; \ 184 | unsigned _count, _bkt_count; \ 185 | char *_prev; \ 186 | struct UT_hash_handle *_thh; \ 187 | if (head) { \ 188 | _count = 0; \ 189 | for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ 190 | _bkt_count = 0; \ 191 | _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ 192 | _prev = NULL; \ 193 | while (_thh) { \ 194 | if (_prev != (char*)(_thh->hh_prev)) { \ 195 | HASH_OOPS("invalid hh_prev %p, actual %p\n", \ 196 | _thh->hh_prev, _prev ); \ 197 | } \ 198 | _bkt_count++; \ 199 | _prev = (char*)(_thh); \ 200 | _thh = _thh->hh_next; \ 201 | } \ 202 | _count += _bkt_count; \ 203 | if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ 204 | HASH_OOPS("invalid bucket count %d, actual %d\n", \ 205 | (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ 206 | } \ 207 | } \ 208 | if (_count != (head)->hh.tbl->num_items) { \ 209 | HASH_OOPS("invalid hh item count %d, actual %d\n", \ 210 | (head)->hh.tbl->num_items, _count ); \ 211 | } \ 212 | /* traverse hh in app order; check next/prev integrity, count */ \ 213 | _count = 0; \ 214 | _prev = NULL; \ 215 | _thh = &(head)->hh; \ 216 | while (_thh) { \ 217 | _count++; \ 218 | if (_prev !=(char*)(_thh->prev)) { \ 219 | HASH_OOPS("invalid prev %p, actual %p\n", \ 220 | _thh->prev, _prev ); \ 221 | } \ 222 | _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ 223 | _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ 224 | (head)->hh.tbl->hho) : NULL ); \ 225 | } \ 226 | if (_count != (head)->hh.tbl->num_items) { \ 227 | HASH_OOPS("invalid app item count %d, actual %d\n", \ 228 | (head)->hh.tbl->num_items, _count ); \ 229 | } \ 230 | } \ 231 | } while (0) 232 | #else 233 | #define HASH_FSCK(hh,head) 234 | #endif 235 | 236 | /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to 237 | * the descriptor to which this macro is defined for tuning the hash function. 238 | * The app can #include to get the prototype for write(2). */ 239 | #ifdef HASH_EMIT_KEYS 240 | #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ 241 | do { \ 242 | unsigned _klen = fieldlen; \ 243 | write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ 244 | write(HASH_EMIT_KEYS, keyptr, fieldlen); \ 245 | } while (0) 246 | #else 247 | #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) 248 | #endif 249 | 250 | /* default to MurmurHash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ 251 | #ifdef HASH_FUNCTION 252 | #define HASH_FCN HASH_FUNCTION 253 | #else 254 | #define HASH_FCN HASH_MUR 255 | #endif 256 | 257 | /* The Bernstein hash function, used in Perl prior to v5.6 */ 258 | #define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ 259 | do { \ 260 | unsigned _hb_keylen=keylen; \ 261 | char *_hb_key=(char*)key; \ 262 | (hashv) = 0; \ 263 | while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ 264 | bkt = (hashv) & (num_bkts-1); \ 265 | } while (0) 266 | 267 | 268 | /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at 269 | * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ 270 | #define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ 271 | do { \ 272 | unsigned _sx_i; \ 273 | char *_hs_key=(char*)key; \ 274 | hashv = 0; \ 275 | for(_sx_i=0; _sx_i < keylen; _sx_i++) \ 276 | hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ 277 | bkt = hashv & (num_bkts-1); \ 278 | } while (0) 279 | 280 | #define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ 281 | do { \ 282 | unsigned _fn_i; \ 283 | char *_hf_key=(char*)key; \ 284 | hashv = 2166136261UL; \ 285 | for(_fn_i=0; _fn_i < keylen; _fn_i++) \ 286 | hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ 287 | bkt = hashv & (num_bkts-1); \ 288 | } while(0); 289 | 290 | #define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ 291 | do { \ 292 | unsigned _ho_i; \ 293 | char *_ho_key=(char*)key; \ 294 | hashv = 0; \ 295 | for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ 296 | hashv += _ho_key[_ho_i]; \ 297 | hashv += (hashv << 10); \ 298 | hashv ^= (hashv >> 6); \ 299 | } \ 300 | hashv += (hashv << 3); \ 301 | hashv ^= (hashv >> 11); \ 302 | hashv += (hashv << 15); \ 303 | bkt = hashv & (num_bkts-1); \ 304 | } while(0) 305 | 306 | #define HASH_JEN_MIX(a,b,c) \ 307 | do { \ 308 | a -= b; a -= c; a ^= ( c >> 13 ); \ 309 | b -= c; b -= a; b ^= ( a << 8 ); \ 310 | c -= a; c -= b; c ^= ( b >> 13 ); \ 311 | a -= b; a -= c; a ^= ( c >> 12 ); \ 312 | b -= c; b -= a; b ^= ( a << 16 ); \ 313 | c -= a; c -= b; c ^= ( b >> 5 ); \ 314 | a -= b; a -= c; a ^= ( c >> 3 ); \ 315 | b -= c; b -= a; b ^= ( a << 10 ); \ 316 | c -= a; c -= b; c ^= ( b >> 15 ); \ 317 | } while (0) 318 | 319 | #define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ 320 | do { \ 321 | unsigned _hj_i,_hj_j,_hj_k; \ 322 | char *_hj_key=(char*)key; \ 323 | hashv = 0xfeedbeef; \ 324 | _hj_i = _hj_j = 0x9e3779b9; \ 325 | _hj_k = keylen; \ 326 | while (_hj_k >= 12) { \ 327 | _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ 328 | + ( (unsigned)_hj_key[2] << 16 ) \ 329 | + ( (unsigned)_hj_key[3] << 24 ) ); \ 330 | _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ 331 | + ( (unsigned)_hj_key[6] << 16 ) \ 332 | + ( (unsigned)_hj_key[7] << 24 ) ); \ 333 | hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ 334 | + ( (unsigned)_hj_key[10] << 16 ) \ 335 | + ( (unsigned)_hj_key[11] << 24 ) ); \ 336 | \ 337 | HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ 338 | \ 339 | _hj_key += 12; \ 340 | _hj_k -= 12; \ 341 | } \ 342 | hashv += keylen; \ 343 | switch ( _hj_k ) { \ 344 | case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ 345 | case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ 346 | case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ 347 | case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ 348 | case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ 349 | case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ 350 | case 5: _hj_j += _hj_key[4]; \ 351 | case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ 352 | case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ 353 | case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ 354 | case 1: _hj_i += _hj_key[0]; \ 355 | } \ 356 | HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ 357 | bkt = hashv & (num_bkts-1); \ 358 | } while(0) 359 | 360 | /* The Paul Hsieh hash function */ 361 | #undef get16bits 362 | #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ 363 | || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) 364 | #define get16bits(d) (*((const uint16_t *) (d))) 365 | #endif 366 | 367 | #if !defined (get16bits) 368 | #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ 369 | +(uint32_t)(((const uint8_t *)(d))[0]) ) 370 | #endif 371 | #define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ 372 | do { \ 373 | char *_sfh_key=(char*)key; \ 374 | hashv = 0xcafebabe; \ 375 | uint32_t _sfh_tmp, _sfh_len = keylen; \ 376 | \ 377 | int _sfh_rem = _sfh_len & 3; \ 378 | _sfh_len >>= 2; \ 379 | \ 380 | /* Main loop */ \ 381 | for (;_sfh_len > 0; _sfh_len--) { \ 382 | hashv += get16bits (_sfh_key); \ 383 | _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ 384 | hashv = (hashv << 16) ^ _sfh_tmp; \ 385 | _sfh_key += 2*sizeof (uint16_t); \ 386 | hashv += hashv >> 11; \ 387 | } \ 388 | \ 389 | /* Handle end cases */ \ 390 | switch (_sfh_rem) { \ 391 | case 3: hashv += get16bits (_sfh_key); \ 392 | hashv ^= hashv << 16; \ 393 | hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ 394 | hashv += hashv >> 11; \ 395 | break; \ 396 | case 2: hashv += get16bits (_sfh_key); \ 397 | hashv ^= hashv << 11; \ 398 | hashv += hashv >> 17; \ 399 | break; \ 400 | case 1: hashv += *_sfh_key; \ 401 | hashv ^= hashv << 10; \ 402 | hashv += hashv >> 1; \ 403 | } \ 404 | \ 405 | /* Force "avalanching" of final 127 bits */ \ 406 | hashv ^= hashv << 3; \ 407 | hashv += hashv >> 5; \ 408 | hashv ^= hashv << 4; \ 409 | hashv += hashv >> 17; \ 410 | hashv ^= hashv << 25; \ 411 | hashv += hashv >> 6; \ 412 | bkt = hashv & (num_bkts-1); \ 413 | } while(0); 414 | 415 | /* Austin Appleby's MurmurHash */ 416 | #define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ 417 | do { \ 418 | const unsigned int _mur_m = 0x5bd1e995; \ 419 | const int _mur_r = 24; \ 420 | hashv = 0xcafebabe ^ keylen; \ 421 | char *_mur_key = (char *)key; \ 422 | uint32_t _mur_tmp, _mur_len = keylen; \ 423 | \ 424 | for (;_mur_len >= 4; _mur_len-=4) { \ 425 | _mur_tmp = *(uint32_t *)_mur_key; \ 426 | _mur_tmp *= _mur_m; \ 427 | _mur_tmp ^= _mur_tmp >> _mur_r; \ 428 | _mur_tmp *= _mur_m; \ 429 | hashv *= _mur_m; \ 430 | hashv ^= _mur_tmp; \ 431 | _mur_key += 4; \ 432 | } \ 433 | \ 434 | switch(_mur_len) \ 435 | { \ 436 | case 3: hashv ^= _mur_key[2] << 16; \ 437 | case 2: hashv ^= _mur_key[1] << 8; \ 438 | case 1: hashv ^= _mur_key[0]; \ 439 | hashv *= _mur_m; \ 440 | }; \ 441 | \ 442 | hashv ^= hashv >> 13; \ 443 | hashv *= _mur_m; \ 444 | hashv ^= hashv >> 15; \ 445 | \ 446 | bkt = hashv & (num_bkts-1); \ 447 | } while(0) 448 | 449 | /* key comparison function; return 0 if keys equal */ 450 | #define HASH_KEYCMP(a,b,len) memcmp(a,b,len) 451 | 452 | /* iterate over items in a known bucket to find desired item */ 453 | #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ 454 | out = TYPEOF(out)((head.hh_head) ? ELMT_FROM_HH(tbl,head.hh_head) : NULL); \ 455 | while (out) { \ 456 | if (out->hh.keylen == keylen_in) { \ 457 | if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \ 458 | } \ 459 | out= TYPEOF(out)((out->hh.hh_next) ? \ 460 | ELMT_FROM_HH(tbl,out->hh.hh_next) : NULL); \ 461 | } 462 | 463 | /* add an item to a bucket */ 464 | #define HASH_ADD_TO_BKT(head,addhh) \ 465 | do { \ 466 | head.count++; \ 467 | (addhh)->hh_next = head.hh_head; \ 468 | (addhh)->hh_prev = NULL; \ 469 | if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ 470 | (head).hh_head=addhh; \ 471 | if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ 472 | && (addhh)->tbl->noexpand != 1) { \ 473 | HASH_EXPAND_BUCKETS((addhh)->tbl); \ 474 | } \ 475 | } while(0) 476 | 477 | /* remove an item from a given bucket */ 478 | #define HASH_DEL_IN_BKT(hh,head,hh_del) \ 479 | (head).count--; \ 480 | if ((head).hh_head == hh_del) { \ 481 | (head).hh_head = hh_del->hh_next; \ 482 | } \ 483 | if (hh_del->hh_prev) { \ 484 | hh_del->hh_prev->hh_next = hh_del->hh_next; \ 485 | } \ 486 | if (hh_del->hh_next) { \ 487 | hh_del->hh_next->hh_prev = hh_del->hh_prev; \ 488 | } 489 | 490 | /* Bucket expansion has the effect of doubling the number of buckets 491 | * and redistributing the items into the new buckets. Ideally the 492 | * items will distribute more or less evenly into the new buckets 493 | * (the extent to which this is true is a measure of the quality of 494 | * the hash function as it applies to the key domain). 495 | * 496 | * With the items distributed into more buckets, the chain length 497 | * (item count) in each bucket is reduced. Thus by expanding buckets 498 | * the hash keeps a bound on the chain length. This bounded chain 499 | * length is the essence of how a hash provides constant time lookup. 500 | * 501 | * The calculation of tbl->ideal_chain_maxlen below deserves some 502 | * explanation. First, keep in mind that we're calculating the ideal 503 | * maximum chain length based on the *new* (doubled) bucket count. 504 | * In fractions this is just n/b (n=number of items,b=new num buckets). 505 | * Since the ideal chain length is an integer, we want to calculate 506 | * ceil(n/b). We don't depend on floating point arithmetic in this 507 | * hash, so to calculate ceil(n/b) with integers we could write 508 | * 509 | * ceil(n/b) = (n/b) + ((n%b)?1:0) 510 | * 511 | * and in fact a previous version of this hash did just that. 512 | * But now we have improved things a bit by recognizing that b is 513 | * always a power of two. We keep its base 2 log handy (call it lb), 514 | * so now we can write this with a bit shift and logical AND: 515 | * 516 | * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) 517 | * 518 | */ 519 | #define HASH_EXPAND_BUCKETS(tbl) \ 520 | do { \ 521 | unsigned _he_bkt; \ 522 | unsigned _he_bkt_i; \ 523 | struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ 524 | UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ 525 | _he_new_buckets = (UT_hash_bucket*)uthash_bkt_malloc( \ 526 | 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ 527 | if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ 528 | memset(_he_new_buckets, 0, \ 529 | 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ 530 | tbl->ideal_chain_maxlen = \ 531 | (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ 532 | ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ 533 | tbl->nonideal_items = 0; \ 534 | for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ 535 | { \ 536 | _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ 537 | while (_he_thh) { \ 538 | _he_hh_nxt = _he_thh->hh_next; \ 539 | HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ 540 | _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ 541 | if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ 542 | tbl->nonideal_items++; \ 543 | _he_newbkt->expand_mult = _he_newbkt->count / \ 544 | tbl->ideal_chain_maxlen; \ 545 | } \ 546 | _he_thh->hh_prev = NULL; \ 547 | _he_thh->hh_next = _he_newbkt->hh_head; \ 548 | if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ 549 | _he_thh; \ 550 | _he_newbkt->hh_head = _he_thh; \ 551 | _he_thh = _he_hh_nxt; \ 552 | } \ 553 | } \ 554 | tbl->num_buckets *= 2; \ 555 | tbl->log2_num_buckets++; \ 556 | uthash_bkt_free( tbl->buckets ); \ 557 | tbl->buckets = _he_new_buckets; \ 558 | tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ 559 | (tbl->ineff_expands+1) : 0; \ 560 | if (tbl->ineff_expands > 1) { \ 561 | tbl->noexpand=1; \ 562 | uthash_noexpand_fyi(tbl); \ 563 | } \ 564 | uthash_expand_fyi(tbl); \ 565 | } while(0) 566 | 567 | 568 | /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ 569 | /* Note that HASH_SORT assumes the hash handle name to be hh. 570 | * HASH_SRT was added to allow the hash handle name to be passed in. */ 571 | #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) 572 | #define HASH_SRT(hh,head,cmpfcn) \ 573 | do { \ 574 | unsigned _hs_i; \ 575 | unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ 576 | struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ 577 | if (head) { \ 578 | _hs_insize = 1; \ 579 | _hs_looping = 1; \ 580 | _hs_list = &((head)->hh); \ 581 | while (_hs_looping) { \ 582 | _hs_p = _hs_list; \ 583 | _hs_list = NULL; \ 584 | _hs_tail = NULL; \ 585 | _hs_nmerges = 0; \ 586 | while (_hs_p) { \ 587 | _hs_nmerges++; \ 588 | _hs_q = _hs_p; \ 589 | _hs_psize = 0; \ 590 | for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ 591 | _hs_psize++; \ 592 | _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ 593 | ((void*)((char*)(_hs_q->next) + \ 594 | (head)->hh.tbl->hho)) : NULL); \ 595 | if (! (_hs_q) ) break; \ 596 | } \ 597 | _hs_qsize = _hs_insize; \ 598 | while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ 599 | if (_hs_psize == 0) { \ 600 | _hs_e = _hs_q; \ 601 | _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ 602 | ((void*)((char*)(_hs_q->next) + \ 603 | (head)->hh.tbl->hho)) : NULL); \ 604 | _hs_qsize--; \ 605 | } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ 606 | _hs_e = _hs_p; \ 607 | _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ 608 | ((void*)((char*)(_hs_p->next) + \ 609 | (head)->hh.tbl->hho)) : NULL); \ 610 | _hs_psize--; \ 611 | } else if (( \ 612 | cmpfcn(TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ 613 | TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ 614 | ) <= 0) { \ 615 | _hs_e = _hs_p; \ 616 | _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ 617 | ((void*)((char*)(_hs_p->next) + \ 618 | (head)->hh.tbl->hho)) : NULL); \ 619 | _hs_psize--; \ 620 | } else { \ 621 | _hs_e = _hs_q; \ 622 | _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ 623 | ((void*)((char*)(_hs_q->next) + \ 624 | (head)->hh.tbl->hho)) : NULL); \ 625 | _hs_qsize--; \ 626 | } \ 627 | if ( _hs_tail ) { \ 628 | _hs_tail->next = ((_hs_e) ? \ 629 | ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ 630 | } else { \ 631 | _hs_list = _hs_e; \ 632 | } \ 633 | _hs_e->prev = ((_hs_tail) ? \ 634 | ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ 635 | _hs_tail = _hs_e; \ 636 | } \ 637 | _hs_p = _hs_q; \ 638 | } \ 639 | _hs_tail->next = NULL; \ 640 | if ( _hs_nmerges <= 1 ) { \ 641 | _hs_looping=0; \ 642 | (head)->hh.tbl->tail = _hs_tail; \ 643 | (head) = TYPEOF(head)ELMT_FROM_HH((head)->hh.tbl, _hs_list); \ 644 | } \ 645 | _hs_insize *= 2; \ 646 | } \ 647 | HASH_FSCK(hh,head); \ 648 | } \ 649 | } while (0) 650 | 651 | /* This function selects items from one hash into another hash. 652 | * The end result is that the selected items have dual presence 653 | * in both hashes. There is no copy of the items made; rather 654 | * they are added into the new hash through a secondary hash 655 | * hash handle that must be present in the structure. */ 656 | #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ 657 | do { \ 658 | unsigned _src_bkt, _dst_bkt; \ 659 | void *_last_elt=NULL, *_elt; \ 660 | UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ 661 | ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ 662 | if (src) { \ 663 | for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ 664 | for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ 665 | _src_hh; \ 666 | _src_hh = _src_hh->hh_next) { \ 667 | _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ 668 | if (cond(_elt)) { \ 669 | _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ 670 | _dst_hh->key = _src_hh->key; \ 671 | _dst_hh->keylen = _src_hh->keylen; \ 672 | _dst_hh->hashv = _src_hh->hashv; \ 673 | _dst_hh->prev = _last_elt; \ 674 | _dst_hh->next = NULL; \ 675 | if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ 676 | if (!dst) { \ 677 | dst = TYPEOF(dst)_elt; \ 678 | HASH_MAKE_TABLE(hh_dst,dst); \ 679 | } else { \ 680 | _dst_hh->tbl = (dst)->hh_dst.tbl; \ 681 | } \ 682 | HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ 683 | HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ 684 | (dst)->hh_dst.tbl->num_items++; \ 685 | _last_elt = _elt; \ 686 | _last_elt_hh = _dst_hh; \ 687 | } \ 688 | } \ 689 | } \ 690 | } \ 691 | HASH_FSCK(hh_dst,dst); \ 692 | } while (0) 693 | 694 | #define HASH_CLEAR(hh,head) \ 695 | do { \ 696 | if (head) { \ 697 | uthash_bkt_free((head)->hh.tbl->buckets ); \ 698 | uthash_tbl_free((head)->hh.tbl); \ 699 | (head)=NULL; \ 700 | } \ 701 | } while(0) 702 | 703 | /* obtain a count of items in the hash */ 704 | #define HASH_COUNT(head) HASH_CNT(hh,head) 705 | #define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0) 706 | 707 | typedef struct UT_hash_bucket { 708 | struct UT_hash_handle *hh_head; 709 | unsigned count; 710 | 711 | /* expand_mult is normally set to 0. In this situation, the max chain length 712 | * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If 713 | * the bucket's chain exceeds this length, bucket expansion is triggered). 714 | * However, setting expand_mult to a non-zero value delays bucket expansion 715 | * (that would be triggered by additions to this particular bucket) 716 | * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. 717 | * (The multiplier is simply expand_mult+1). The whole idea of this 718 | * multiplier is to reduce bucket expansions, since they are expensive, in 719 | * situations where we know that a particular bucket tends to be overused. 720 | * It is better to let its chain length grow to a longer yet-still-bounded 721 | * value, than to do an O(n) bucket expansion too often. 722 | */ 723 | unsigned expand_mult; 724 | 725 | } UT_hash_bucket; 726 | 727 | typedef struct UT_hash_table { 728 | UT_hash_bucket *buckets; 729 | unsigned num_buckets, log2_num_buckets; 730 | unsigned num_items; 731 | struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ 732 | ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ 733 | 734 | /* in an ideal situation (all buckets used equally), no bucket would have 735 | * more than ceil(#items/#buckets) items. that's the ideal chain length. */ 736 | unsigned ideal_chain_maxlen; 737 | 738 | /* nonideal_items is the number of items in the hash whose chain position 739 | * exceeds the ideal chain maxlen. these items pay the penalty for an uneven 740 | * hash distribution; reaching them in a chain traversal takes >ideal steps */ 741 | unsigned nonideal_items; 742 | 743 | /* ineffective expands occur when a bucket doubling was performed, but 744 | * afterward, more than half the items in the hash had nonideal chain 745 | * positions. If this happens on two consecutive expansions we inhibit any 746 | * further expansion, as it's not helping; this happens when the hash 747 | * function isn't a good fit for the key domain. When expansion is inhibited 748 | * the hash will still work, albeit no longer in constant time. */ 749 | unsigned ineff_expands, noexpand; 750 | 751 | 752 | } UT_hash_table; 753 | 754 | 755 | typedef struct UT_hash_handle { 756 | struct UT_hash_table *tbl; 757 | void *prev; /* prev element in app order */ 758 | void *next; /* next element in app order */ 759 | struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ 760 | struct UT_hash_handle *hh_next; /* next hh in bucket order */ 761 | void *key; /* ptr to enclosing struct's key */ 762 | unsigned keylen; /* enclosing struct's key len */ 763 | unsigned hashv; /* result of hash-fcn(key) */ 764 | } UT_hash_handle; 765 | 766 | #endif /* UTHASH_H */ 767 | --------------------------------------------------------------------------------