├── doc ├── .gitignore └── SConscript ├── site_scons ├── .gitignore └── checklib.py ├── tests ├── test-protobuf-field ├── .gitignore ├── int-callback.c ├── test-callbacks.h ├── SConscript ├── test-noop.c ├── test-eof.c ├── sum-callback.c ├── test-skip.c ├── indexed-sum-callback.c ├── test-protobuf-skip-length-prefixed.c ├── test-hwm.c ├── test-int.c └── test-double-sum.c ├── .gitignore ├── examples └── genealogy │ ├── person.proto │ ├── person.h │ └── person.c ├── include ├── push.h ├── push │ ├── protobuf.h │ ├── config.h │ ├── protobuf │ │ ├── message.h │ │ ├── combinators.h │ │ ├── primitives.h │ │ ├── basics.h │ │ └── field-map.h │ ├── primitives.h │ ├── pairs.h │ ├── combinators.h │ ├── typesafe_cb.h │ └── pure.h └── SConscript ├── src ├── SConscript ├── noop.c ├── protobuf │ ├── skip-length-prefixed.c │ ├── varint-prefixed.c │ ├── submessage.c │ ├── hwm-string.c │ ├── message.c │ ├── assign.c │ └── varint64.c ├── pairs │ ├── par.c │ ├── both.c │ ├── first.c │ └── second.c ├── eof.c ├── compose.c ├── callback.c ├── fixed.c ├── skip.c ├── parser.c └── hwm-string.c ├── LICENSE.txt └── SConstruct /doc/.gitignore: -------------------------------------------------------------------------------- 1 | html/ 2 | -------------------------------------------------------------------------------- /site_scons/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | -------------------------------------------------------------------------------- /tests/test-protobuf-field: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcreager/libpush/HEAD/tests/test-protobuf-field -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .sconf_temp/ 2 | .sconsign.dblite 3 | .scons.vars* 4 | config.log 5 | 6 | libpush*/ 7 | libpush*.tar.gz 8 | 9 | *.[oa] 10 | *.os 11 | *.so 12 | *.dylib 13 | -------------------------------------------------------------------------------- /examples/genealogy/person.proto: -------------------------------------------------------------------------------- 1 | message Person 2 | { 3 | required uint32 id = 1; 4 | required string name = 2; 5 | optional uint32 mother = 3; 6 | optional uint32 father = 4; 7 | optional uint64 dob = 5; 8 | } 9 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | test-compose 2 | test-double-sum 3 | test-eof 4 | test-hwm 5 | test-indexed-sum 6 | test-int 7 | test-noop 8 | test-pairs 9 | test-skip 10 | test-sum 11 | 12 | test-protobuf-message 13 | test-protobuf-skip-length-prefixed 14 | test-protobuf-submessage 15 | test-protobuf-varint32 16 | test-protobuf-varint64 17 | test-protobuf-varint-size 18 | -------------------------------------------------------------------------------- /include/push.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_H 12 | #define PUSH_H 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #endif /* PUSH_H */ 22 | -------------------------------------------------------------------------------- /doc/SConscript: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | Import('root_env SOURCE_FILES') 4 | 5 | SOURCE_FILES.append(File('SConscript')) 6 | 7 | env = root_env.Clone(ENV={'PATH': os.environ['PATH']}) 8 | 9 | cfg_file = File("libpush.cfg") 10 | SOURCE_FILES.append(cfg_file) 11 | 12 | doc = env.Command("html", cfg_file, 13 | "doxygen %s" % cfg_file.abspath) 14 | env.Alias("doc", doc) 15 | env.AlwaysBuild(doc) 16 | 17 | env.Alias("install-doc", env.Install("$DOCDIR", "html")) 18 | 19 | # Don't build the documentation by default; but clean it by default. 20 | 21 | if GetOption('clean'): 22 | env.Clean("doc", "html") 23 | env.Default("doc") 24 | -------------------------------------------------------------------------------- /tests/int-callback.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | push_callback_t * 19 | integer_callback_new(const char *name, 20 | void *parent, 21 | push_parser_t *parser) 22 | { 23 | if (name == NULL) 24 | name = "integer"; 25 | 26 | return push_fixed_new(name, parent, parser, sizeof(uint32_t)); 27 | } 28 | -------------------------------------------------------------------------------- /include/push/protobuf.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PROTOBUF_H 12 | #define PUSH_PROTOBUF_H 13 | 14 | /** 15 | * @file 16 | * 17 | * Contains callback implementations for parsing Google Protocol 18 | * Buffer messages. 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | #endif /* PUSH_PROTOBUF_H */ 30 | -------------------------------------------------------------------------------- /include/push/config.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_CONFIG_H 12 | #define PUSH_CONFIG_H 13 | 14 | /** 15 | * @file 16 | * 17 | * This file defines the configuration macros that the CCAN talloc and 18 | * typesafe_cb libraries expect. 19 | * 20 | * @todo We should update these so that they're automatically 21 | * calculated by scons. 22 | */ 23 | 24 | #define HAVE_ATTRIBUTE_PRINTF 1 25 | #define HAVE_BUILTIN_CHOOSE_EXPR 1 26 | #define HAVE_BUILTIN_EXPECT 1 27 | #define HAVE_BUILTIN_TYPES_COMPATIBLE 1 28 | #define HAVE_TYPEOF 1 29 | 30 | #endif /* PUSH_CONFIG_H */ 31 | -------------------------------------------------------------------------------- /include/push/protobuf/message.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PROTOBUF_MESSAGE_H 12 | #define PUSH_PROTOBUF_MESSAGE_H 13 | 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | 24 | /** 25 | * Create a new callback for reading a Protocol Buffer message. The 26 | * new callback will use the field callbacks in field_map to read the 27 | * fields of the message. 28 | */ 29 | 30 | push_callback_t * 31 | push_protobuf_message_new(const char *name, 32 | void *parent, 33 | push_parser_t *parser, 34 | push_protobuf_field_map_t *field_map); 35 | 36 | 37 | #endif /* PUSH_PROTOBUF_MESSAGE_H */ 38 | -------------------------------------------------------------------------------- /examples/genealogy/person.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PROTOBUF_EXAMPLE_PERSON_H 12 | #define PUSH_PROTOBUF_EXAMPLE_PERSON_H 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | typedef uint32_t person_id_t; 20 | typedef uint64_t date_t; 21 | 22 | typedef struct _person 23 | { 24 | person_id_t id; 25 | char *name; 26 | person_id_t mother; 27 | person_id_t father; 28 | date_t dob; 29 | } person_t; 30 | 31 | 32 | bool 33 | person_eq(person_t *person1, person_t *person2); 34 | 35 | 36 | /** 37 | * Create a protobuf message parser that reads a Person message into 38 | * the given person_t object. 39 | */ 40 | 41 | push_protobuf_message_t * 42 | create_person_parser(person_t *person); 43 | 44 | 45 | #endif /* PUSH_PROTOBUF_EXAMPLE_PERSON_H */ 46 | -------------------------------------------------------------------------------- /include/push/protobuf/combinators.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PROTOBUF_COMBINATORS_H 12 | #define PUSH_PROTOBUF_COMBINATORS_H 13 | 14 | #include 15 | 16 | 17 | /** 18 | * Create a new callback that wraps another callback, ensuring that no 19 | * more than a certain number of bytes are passed to the wrapped 20 | * callback. This works just like the push_dynamic_max_bytes_new 21 | * combinator, except that the threshold is read in as a 22 | * varint-encoded integer from the parse stream. 23 | */ 24 | 25 | push_callback_t * 26 | push_protobuf_varint_prefixed_new(const char *name, 27 | void *parent, 28 | push_parser_t *parser, 29 | push_callback_t *wrapped); 30 | 31 | 32 | #endif /* PUSH_PROTOBUF_COMBINATORS_H */ 33 | -------------------------------------------------------------------------------- /src/SConscript: -------------------------------------------------------------------------------- 1 | Import('root_env SOURCE_FILES') 2 | 3 | SOURCE_FILES.append(File('SConscript')) 4 | 5 | env = root_env.Clone() 6 | 7 | env.Append(CPPPATH = ["../include", "$libhwm_CPPPATH"], 8 | LIBPATH = [".", "$libhwm_LIBPATH"]) 9 | 10 | libpush_files = map(File, \ 11 | [ 12 | "callback.c", 13 | "compose.c", 14 | "eof.c", 15 | "fixed.c", 16 | "fold.c", 17 | "hwm-string.c", 18 | "max-bytes.c", 19 | "min-bytes.c", 20 | "noop.c", 21 | "pairs/both.c", 22 | "pairs/first.c", 23 | "pairs/par.c", 24 | "pairs/second.c", 25 | "parser.c", 26 | "skip.c", 27 | "talloc.c", 28 | "protobuf/assign.c", 29 | "protobuf/field-map.c", 30 | "protobuf/hwm-string.c", 31 | "protobuf/message.c", 32 | "protobuf/skip-length-prefixed.c", 33 | "protobuf/submessage.c", 34 | "protobuf/varint32.c", 35 | "protobuf/varint64.c", 36 | "protobuf/varint-prefixed.c", 37 | ]) 38 | 39 | SOURCE_FILES.extend(libpush_files) 40 | 41 | libpush = env.SharedLibrary("push", libpush_files, 42 | LIBS=["$libhwm_LIB"]) 43 | env.Alias("install", env.Install("$LIBDIR", libpush)) 44 | Default(libpush) 45 | 46 | Export('libpush') 47 | -------------------------------------------------------------------------------- /include/SConscript: -------------------------------------------------------------------------------- 1 | Import('root_env SOURCE_FILES') 2 | 3 | SOURCE_FILES.append(File('SConscript')) 4 | 5 | env = root_env.Clone() 6 | 7 | env.Alias("install", env.Install("$INCLUDEDIR")) 8 | 9 | 10 | h_files = map(File, \ 11 | [ 12 | "push.h", 13 | ]) 14 | 15 | SOURCE_FILES.extend(h_files) 16 | 17 | env.Alias("install", env.Install("$INCLUDEDIR", 18 | h_files)) 19 | 20 | 21 | push_h_files = map(File, \ 22 | [ 23 | "push/basics.h", 24 | "push/combinators.h", 25 | "push/config.h", 26 | "push/pairs.h", 27 | "push/pure.h", 28 | "push/primitives.h", 29 | "push/protobuf.h", 30 | "push/talloc.h", 31 | "push/typesafe_cb.h", 32 | ]) 33 | 34 | SOURCE_FILES.extend(push_h_files) 35 | 36 | env.Alias("install", env.Install("$INCLUDEDIR/push", 37 | push_h_files)) 38 | 39 | 40 | protobuf_h_files = map(File, \ 41 | [ 42 | "push/protobuf/basics.h", 43 | "push/protobuf/combinators.h", 44 | "push/protobuf/field-map.h", 45 | "push/protobuf/message.h", 46 | "push/protobuf/primitives.h", 47 | ]) 48 | 49 | SOURCE_FILES.extend(protobuf_h_files) 50 | 51 | env.Alias("install", env.Install("$INCLUDEDIR/push/protobuf", 52 | protobuf_h_files)) 53 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2009-2010, RedJack, LLC. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | • Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | • Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | • Neither the name of RedJack Software, LLC nor the names of its 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /tests/test-callbacks.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef TEST_CALLBACKS_H 12 | #define TEST_CALLBACKS_H 13 | 14 | #include 15 | 16 | 17 | /** 18 | * Create a callback that parses a single uint32_t. 19 | */ 20 | 21 | push_callback_t * 22 | integer_callback_new(const char *name, 23 | void *parent, 24 | push_parser_t *parser); 25 | 26 | 27 | /** 28 | * Create a callback that parses a single uint32_t and adds it to a 29 | * running sum. The previous value of the sum is taken in as an 30 | * input; the new value is the output. 31 | */ 32 | 33 | push_callback_t * 34 | sum_callback_new(const char *name, 35 | void *parent, 36 | push_parser_t *parser); 37 | 38 | 39 | /** 40 | * Create a callback that parses a two uint32_ts — an index and a 41 | * value. It then adds the value to the specified element of an array 42 | * of sums. The previous array of sums is taken in as an input; it is 43 | * modified in-place and used as the output. 44 | */ 45 | 46 | push_callback_t * 47 | indexed_sum_callback_new(const char *name, 48 | void *parent, 49 | push_parser_t *parser, 50 | uint32_t num_sums); 51 | 52 | 53 | #endif /* TEST_CALLBACKS_H */ 54 | -------------------------------------------------------------------------------- /examples/genealogy/person.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | 18 | 19 | bool 20 | person_eq(person_t *person1, person_t *person2) 21 | { 22 | if (person1 == person2) 23 | return true; 24 | 25 | return 26 | (person1->id == person2->id) && 27 | (strcmp(person1->name, person2->name) == 0) && 28 | (person1->mother == person2->mother) && 29 | (person1->father == person2->father) && 30 | (person1->dob == person2->dob); 31 | } 32 | 33 | 34 | #define CHECK(call) \ 35 | if (!(call)) \ 36 | { \ 37 | goto error; \ 38 | } 39 | 40 | push_protobuf_message_t * 41 | create_person_parser(person_t *person) 42 | { 43 | push_protobuf_message_t *result = 44 | push_protobuf_message_new(); 45 | 46 | if (result == NULL) goto error; 47 | 48 | CHECK(push_protobuf_assign_uint32(result, 1, &person->id)); 49 | CHECK(push_protobuf_malloc_str(result, 2, &person->name)); 50 | CHECK(push_protobuf_assign_uint32(result, 3, &person->mother)); 51 | CHECK(push_protobuf_assign_uint32(result, 4, &person->father)); 52 | CHECK(push_protobuf_assign_uint64(result, 5, &person->dob)); 53 | 54 | return result; 55 | 56 | error: 57 | if (result != NULL) 58 | push_callback_free(&result->base); 59 | 60 | return NULL; 61 | } 62 | -------------------------------------------------------------------------------- /src/noop.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | /** 19 | * The user data struct for a noop callback. 20 | */ 21 | 22 | typedef struct _noop 23 | { 24 | /** 25 | * The push_callback_t superclass for this callback. 26 | */ 27 | 28 | push_callback_t callback; 29 | 30 | } noop_t; 31 | 32 | 33 | static void 34 | noop_activate(void *user_data, 35 | void *result, 36 | const void *buf, 37 | size_t bytes_remaining) 38 | { 39 | noop_t *noop = (noop_t *) user_data; 40 | 41 | PUSH_DEBUG_MSG("%s: Activating.\n", 42 | push_talloc_get_name(noop)); 43 | 44 | /* 45 | * Immediately succeed with the same input value. 46 | */ 47 | 48 | push_continuation_call(noop->callback.success, 49 | result, 50 | buf, 51 | bytes_remaining); 52 | 53 | return; 54 | } 55 | 56 | 57 | push_callback_t * 58 | push_noop_new(const char *name, 59 | void *parent, 60 | push_parser_t *parser) 61 | { 62 | noop_t *noop = push_talloc(parent, noop_t); 63 | 64 | if (noop == NULL) 65 | return NULL; 66 | 67 | if (name == NULL) name = "noop"; 68 | push_talloc_set_name_const(noop, name); 69 | 70 | push_callback_init(&noop->callback, parser, noop, 71 | noop_activate, 72 | NULL, NULL, NULL); 73 | 74 | return &noop->callback; 75 | } 76 | -------------------------------------------------------------------------------- /site_scons/checklib.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Provides a function similar to CheckLib, but allows you to specify the 5 | directory where we expect to find the library. Also defines a 6 | command-line option that lets the user override this directory. 7 | """ 8 | 9 | from SCons.Script import * 10 | 11 | def CheckLibInPath(context, libname, library, call, header=""): 12 | vars = Variables('.scons.vars.%s' % libname, ARGUMENTS) 13 | 14 | vars.AddVariables( 15 | PathVariable("with_%s" % libname, 16 | "Location of %s library" % libname, 17 | "/usr"), 18 | ) 19 | 20 | vars.Update(context.env) 21 | vars.Save('.scons.vars.%s' % libname, context.env) 22 | 23 | context.Message("Checking for %s..." % libname) 24 | 25 | try: 26 | lastLIBS = context.env['LIBS'] 27 | except KeyError: 28 | lastLIBS = [] 29 | 30 | try: 31 | lastLIBPATH = context.env['LIBPATH'] 32 | except KeyError: 33 | lastLIBPATH = [] 34 | 35 | try: 36 | lastCPPPATH = context.env['CPPPATH'] 37 | except KeyError: 38 | lastCPPPATH = [] 39 | 40 | libpath = "${with_%s}/lib" % libname 41 | cpppath = "${with_%s}/include" % libname 42 | 43 | context.env.Append(LIBS=[library], 44 | LIBPATH=[libpath], 45 | CPPPATH=[cpppath]) 46 | 47 | ret = context.TryLink(""" 48 | %s 49 | 50 | int 51 | main(int argc, char **argv) { 52 | %s; 53 | return 0; 54 | } 55 | """ % (header, call), ".c") 56 | 57 | context.env.Replace(LIBS=lastLIBS, 58 | LIBPATH=lastLIBPATH, 59 | CPPPATH=lastCPPPATH) 60 | 61 | if ret: 62 | context.env['%s_LIB' % libname] = library 63 | context.env['%s_LIBPATH' % libname] = libpath 64 | context.env['%s_CPPPATH' % libname] = cpppath 65 | 66 | context.Result(ret) 67 | return ret 68 | -------------------------------------------------------------------------------- /src/protobuf/skip-length-prefixed.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | 19 | push_callback_t * 20 | push_protobuf_skip_length_prefixed_new(const char *name, 21 | void *parent, 22 | push_parser_t *parser) 23 | { 24 | void *context; 25 | push_callback_t *read_size = NULL; 26 | push_callback_t *skip = NULL; 27 | push_callback_t *compose = NULL; 28 | 29 | /* 30 | * Create a memory context for the objects we're about to create. 31 | */ 32 | 33 | context = push_talloc_new(parent); 34 | if (context == NULL) return NULL; 35 | 36 | /* 37 | * Create the callbacks. 38 | */ 39 | 40 | if (name == NULL) name = "pb-skip-lp"; 41 | 42 | read_size = push_protobuf_varint_size_new 43 | (push_talloc_asprintf(context, "%s.size", name), 44 | context, parser); 45 | skip = push_skip_new 46 | (push_talloc_asprintf(context, "%s.skip", name), 47 | context, parser); 48 | compose = push_compose_new 49 | (push_talloc_asprintf(context, "%s.compose", name), 50 | context, parser, read_size, skip); 51 | 52 | /* 53 | * Because of NULL propagation, we only have to check the last 54 | * result to see if everything was created okay. 55 | */ 56 | 57 | if (compose == NULL) goto error; 58 | return compose; 59 | 60 | error: 61 | /* 62 | * Before returning, free any objects we created before the error. 63 | */ 64 | 65 | push_talloc_free(context); 66 | return NULL; 67 | } 68 | -------------------------------------------------------------------------------- /tests/SConscript: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | 4 | Import('root_env SOURCE_FILES') 5 | 6 | SOURCE_FILES.append(File('SConscript')) 7 | 8 | env = root_env.Clone() 9 | 10 | env.Prepend(CPPPATH=[".", "../include", 11 | "$check_CPPPATH", "$libhwm_CPPPATH"], 12 | LIBPATH=[".", "../src", "$check_LIBPATH", "$libhwm_LIBPATH"]) 13 | 14 | 15 | # The library with the common test callbacks. 16 | 17 | libpushtests_files = map(File, \ 18 | [ 19 | "indexed-sum-callback.c", 20 | "int-callback.c", 21 | "sum-callback.c", 22 | ]) 23 | 24 | SOURCE_FILES.extend(libpushtests_files) 25 | SOURCE_FILES.append(File("test-callbacks.h")) 26 | 27 | libpushtests = env.StaticLibrary("pushtests", libpushtests_files, 28 | LIBS=['push']) 29 | 30 | 31 | # Give each test program an RPATH, so that it can find the libpush 32 | # library while they're still in the source tree. 33 | 34 | rpath = [env.Literal(os.path.join('\\$$ORIGIN', os.pardir, 'src'))] 35 | 36 | 37 | def add_test(test_program): 38 | c_file = "%s.c" % test_program 39 | SOURCE_FILES.append(File(c_file)) 40 | 41 | target = env.Program(test_program, [c_file], 42 | LIBS=['push', libpushtests, 43 | '$check_LIB', '$libhwm_LIB'], 44 | RPATH=rpath) 45 | env.Alias("build-tests", target) 46 | 47 | run_test_target = env.Alias(test_program, [target], 48 | ["@%s" % target[0].abspath]) 49 | env.Alias("test", run_test_target) 50 | env.AlwaysBuild(run_test_target) 51 | 52 | 53 | add_test("test-compose") 54 | add_test("test-double-sum") 55 | add_test("test-eof") 56 | add_test("test-hwm") 57 | add_test("test-indexed-sum") 58 | add_test("test-int") 59 | add_test("test-noop") 60 | add_test("test-pairs") 61 | add_test("test-skip") 62 | add_test("test-sum") 63 | 64 | add_test("test-protobuf-message") 65 | add_test("test-protobuf-skip-length-prefixed") 66 | add_test("test-protobuf-submessage") 67 | add_test("test-protobuf-varint32") 68 | add_test("test-protobuf-varint64") 69 | add_test("test-protobuf-varint-size") 70 | 71 | 72 | # Don't build the tests by default; but clean them by default. 73 | 74 | if GetOption('clean'): 75 | env.Default("build-tests") 76 | -------------------------------------------------------------------------------- /src/pairs/par.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | push_callback_t * 18 | push_par_new(const char *name, 19 | void *parent, 20 | push_parser_t *parser, 21 | push_callback_t *a, 22 | push_callback_t *b) 23 | { 24 | /* 25 | * For now, we just implement this using the definition from 26 | * Hughes's paper: 27 | * 28 | * a *** b = first a >>> second b 29 | */ 30 | 31 | void *context; 32 | push_callback_t *first; 33 | push_callback_t *second; 34 | push_callback_t *callback; 35 | 36 | /* 37 | * If either wrapped callback is NULL, return NULL ourselves. 38 | */ 39 | 40 | if ((a == NULL) || (b == NULL)) 41 | return NULL; 42 | 43 | /* 44 | * Create a memory context for the objects we're about to create. 45 | */ 46 | 47 | context = push_talloc_new(parent); 48 | if (context == NULL) return NULL; 49 | 50 | /* 51 | * Create the callbacks. 52 | */ 53 | 54 | if (name == NULL) name = "par"; 55 | 56 | first = push_first_new 57 | (push_talloc_asprintf(context, "%s.first", name), 58 | context, parser, a); 59 | second = push_second_new 60 | (push_talloc_asprintf(context, "%s.second", name), 61 | context, parser, b); 62 | callback = push_compose_new 63 | (push_talloc_asprintf(context, "%s.compose", name), 64 | context, parser, first, second); 65 | 66 | /* 67 | * Because of NULL propagation, we only have to check the last 68 | * result to see if everything was created okay. 69 | */ 70 | 71 | if (callback == NULL) goto error; 72 | return callback; 73 | 74 | error: 75 | /* 76 | * Before returning, free any objects we created before the error. 77 | */ 78 | 79 | push_talloc_free(context); 80 | return NULL; 81 | } 82 | -------------------------------------------------------------------------------- /include/push/protobuf/primitives.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PROTOBUF_PRIMITIVES_H 12 | #define PUSH_PROTOBUF_PRIMITIVES_H 13 | 14 | #include 15 | 16 | #include 17 | 18 | 19 | /** 20 | * Create a new callback that reads a length-prefixed Protocol Buffer 21 | * string into a high-water mark buffer. 22 | */ 23 | 24 | push_callback_t * 25 | push_protobuf_hwm_string_new(const char *name, 26 | void *parent, 27 | push_parser_t *parser, 28 | hwm_buffer_t *buf); 29 | 30 | 31 | /** 32 | * Create a new callback that skips over a length-prefixed Protocol 33 | * Buffers field. 34 | */ 35 | 36 | push_callback_t * 37 | push_protobuf_skip_length_prefixed_new(const char *name, 38 | void *parent, 39 | push_parser_t *parser); 40 | 41 | 42 | /** 43 | * Create a new callback for parsing a varint-encoded integer, which 44 | * we don't expect to be more than 32 bits. The result pointer will 45 | * point at the parsed value, stored as a uint32_t. 46 | */ 47 | 48 | push_callback_t * 49 | push_protobuf_varint32_new(const char *name, 50 | void *parent, 51 | push_parser_t *parser); 52 | 53 | 54 | /** 55 | * Create a new callback for parsing a varint-encoded integer, which 56 | * we don't expect to be more than 64 bits. The result pointer will 57 | * point at the parsed value, stored as a uint64_t. 58 | */ 59 | 60 | push_callback_t * 61 | push_protobuf_varint64_new(const char *name, 62 | void *parent, 63 | push_parser_t *parser); 64 | 65 | 66 | /** 67 | * Create a new callback for parsing a varint-encoded integer, which 68 | * we will use as a size. The result pointer will point at the parsed 69 | * value, stored as a size_t. 70 | */ 71 | 72 | push_callback_t * 73 | push_protobuf_varint_size_new(const char *name, 74 | void *parent, 75 | push_parser_t *parser); 76 | 77 | 78 | #endif /* PUSH_PROTOBUF_PRIMITIVES_H */ 79 | -------------------------------------------------------------------------------- /src/protobuf/varint-prefixed.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | 20 | push_callback_t * 21 | push_protobuf_varint_prefixed_new(const char *name, 22 | void *parent, 23 | push_parser_t *parser, 24 | push_callback_t *wrapped) 25 | { 26 | void *context; 27 | push_callback_t *dup; 28 | push_callback_t *size; 29 | push_callback_t *first; 30 | push_callback_t *max_bytes; 31 | push_callback_t *compose1; 32 | push_callback_t *compose2; 33 | 34 | /* 35 | * If the wrapped callback is NULL, return NULL ourselves. 36 | */ 37 | 38 | if (wrapped == NULL) 39 | return NULL; 40 | 41 | /* 42 | * Create a memory context for the objects we're about to create. 43 | */ 44 | 45 | context = push_talloc_new(parent); 46 | if (context == NULL) return NULL; 47 | 48 | /* 49 | * Then create the callbacks. 50 | */ 51 | 52 | if (name == NULL) name = "varint-prefixed"; 53 | 54 | dup = push_dup_new 55 | (push_talloc_asprintf(context, "%s.dup", name), 56 | context, parser); 57 | size = push_protobuf_varint_size_new 58 | (push_talloc_asprintf(context, "%s.size", name), 59 | context, parser); 60 | first = push_first_new 61 | (push_talloc_asprintf(context, "%s.first", name), 62 | context, parser, size); 63 | compose1 = push_compose_new 64 | (push_talloc_asprintf(context, "%s.compose1", name), 65 | context, parser, dup, first); 66 | max_bytes = push_dynamic_max_bytes_new 67 | (push_talloc_asprintf(context, "%s.max", name), 68 | context, parser, wrapped); 69 | compose2 = push_compose_new 70 | (push_talloc_asprintf(context, "%s.compose2", name), 71 | context, parser, compose1, max_bytes); 72 | 73 | /* 74 | * Because of NULL propagation, we only have to check the last 75 | * result to see if everything was created okay. 76 | */ 77 | 78 | if (compose2 == NULL) goto error; 79 | return compose2; 80 | 81 | error: 82 | /* 83 | * Before returning, free any objects we created before the error. 84 | */ 85 | 86 | push_talloc_free(context); 87 | return NULL; 88 | } 89 | -------------------------------------------------------------------------------- /src/protobuf/submessage.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | bool 21 | push_protobuf_add_submessage(const char *message_name, 22 | const char *field_name, 23 | void *parent, 24 | push_parser_t *parser, 25 | push_protobuf_field_map_t *field_map, 26 | push_protobuf_tag_number_t field_number, 27 | push_callback_t *message_callback) 28 | { 29 | void *context; 30 | const char *full_field_name; 31 | push_callback_t *varint_prefixed = NULL; 32 | 33 | /* 34 | * If the field map or message callback is NULL, return false. 35 | */ 36 | 37 | if ((field_map == NULL) || (message_callback == NULL)) 38 | return NULL; 39 | 40 | /* 41 | * Create a memory context for the objects we're about to create. 42 | */ 43 | 44 | context = push_talloc_new(parent); 45 | if (context == NULL) return NULL; 46 | 47 | /* 48 | * Create the callbacks. 49 | */ 50 | 51 | if (message_name == NULL) message_name = "message"; 52 | if (field_name == NULL) field_name = ".submessage"; 53 | 54 | full_field_name = 55 | push_talloc_asprintf(context, "%s.%s", 56 | message_name, field_name); 57 | 58 | varint_prefixed = push_protobuf_varint_prefixed_new 59 | (push_talloc_asprintf(context, "%s.dup", 60 | full_field_name), 61 | context, parser, message_callback); 62 | 63 | /* 64 | * Because of NULL propagation, we only have to check the last 65 | * result to see if everything was created okay. 66 | */ 67 | 68 | if (varint_prefixed == NULL) goto error; 69 | 70 | /* 71 | * Try to add the new field. If we can't, free the compose before 72 | * returning. 73 | */ 74 | 75 | if (!push_protobuf_field_map_add_field 76 | (full_field_name, parser, field_map, field_number, 77 | PUSH_PROTOBUF_TAG_TYPE_LENGTH_DELIMITED, 78 | varint_prefixed)) 79 | { 80 | goto error; 81 | } 82 | 83 | return true; 84 | 85 | error: 86 | /* 87 | * Before returning, free any objects we created before the error. 88 | */ 89 | 90 | push_talloc_free(context); 91 | return false; 92 | } 93 | -------------------------------------------------------------------------------- /include/push/primitives.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PRIMITIVES_H 12 | #define PUSH_PRIMITIVES_H 13 | 14 | /** 15 | * @file 16 | * 17 | * This file defines the built-in primitive parser callbacks. These 18 | * callbacks operator on their own, and do not wrap any other 19 | * callbacks. 20 | */ 21 | 22 | #include 23 | 24 | #include 25 | 26 | 27 | /** 28 | * Create a new callback that requires the end of the stream. If any 29 | * data is present, it results in a parse error. 30 | */ 31 | 32 | push_callback_t * 33 | push_eof_new(const char *name, 34 | void *parent, 35 | push_parser_t *parser); 36 | 37 | 38 | /** 39 | * Create a new callback that reads in a fixed amount of data into a 40 | * buffer. This can be used to read constant-sized data structures, 41 | * for instance. The result pointer is allowed to point directly into 42 | * the data chunk, so the caller should copy this into a separate 43 | * buffer if the data needs to be available across multiple data 44 | * chunks. 45 | */ 46 | 47 | push_callback_t * 48 | push_fixed_new(const char *name, 49 | void *parent, 50 | push_parser_t *parser, 51 | size_t size); 52 | 53 | 54 | /** 55 | * Create a new callback that reads a string into a high-water mark 56 | * buffer. This callback doesn't do anything to determine the length 57 | * of the string; instead, it takes in a pointer to a size_t as input, 58 | * and uses that as the length of the string. The callback's result 59 | * will be a pointer to the data in the HWM buffer — not a pointer to 60 | * the hwm_buffer_t object itself. We will ensure that there is a NUL 61 | * pointer at the end of the string. 62 | */ 63 | 64 | push_callback_t * 65 | push_hwm_string_new(const char *name, 66 | void *parent, 67 | push_parser_t *parser, 68 | hwm_buffer_t *buf); 69 | 70 | 71 | /** 72 | * Create a new callback that does nothing. It parses no data, and 73 | * copies its input to its output. 74 | */ 75 | 76 | push_callback_t * 77 | push_noop_new(const char *name, 78 | void *parent, 79 | push_parser_t *parser); 80 | 81 | 82 | /** 83 | * Create a new callback that skips the specified number of bytes. 84 | * The callback's input should be a pointer to a size_t, indicating 85 | * the number of bytes to skip. 86 | */ 87 | 88 | push_callback_t * 89 | push_skip_new(const char *name, 90 | void *parent, 91 | push_parser_t *parser); 92 | 93 | 94 | #endif /* PUSH_PRIMITIVES_H */ 95 | -------------------------------------------------------------------------------- /src/pairs/both.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | static bool 20 | duplicate(push_pair_t *result, void *input, void **output) 21 | { 22 | result->first = input; 23 | result->second = input; 24 | 25 | *output = result; 26 | return true; 27 | } 28 | 29 | 30 | push_define_pure_data_callback(dup_new, duplicate, "dup", 31 | void, void, push_pair_t); 32 | 33 | 34 | push_callback_t * 35 | push_dup_new(const char *name, 36 | void *parent, 37 | push_parser_t *parser) 38 | { 39 | return dup_new(name, parent, parser, NULL); 40 | } 41 | 42 | 43 | /* 44 | * For now, we just implement the “both” combinator using the 45 | * definition from Hughes's paper: 46 | * 47 | * a &&& b = arr (\a -> (a,a)) >>> (a *** b) 48 | */ 49 | 50 | push_callback_t * 51 | push_both_new(const char *name, 52 | void *parent, 53 | push_parser_t *parser, 54 | push_callback_t *a, 55 | push_callback_t *b) 56 | { 57 | void *context; 58 | push_callback_t *dup; 59 | push_callback_t *par; 60 | push_callback_t *callback; 61 | 62 | /* 63 | * If either wrapped callback is NULL, return NULL ourselves. 64 | */ 65 | 66 | if ((a == NULL) || (b == NULL)) 67 | return NULL; 68 | 69 | /* 70 | * Create a memory context for the objects we're about to create. 71 | */ 72 | 73 | context = push_talloc_new(parent); 74 | if (context == NULL) return NULL; 75 | 76 | /* 77 | * Create the callbacks. 78 | */ 79 | 80 | if (name == NULL) name = "both"; 81 | 82 | dup = push_dup_new 83 | (push_talloc_asprintf(context, "%s.dup", name), 84 | context, parser); 85 | par = push_par_new 86 | (push_talloc_asprintf(context, "%s.par", name), 87 | context, parser, a, b); 88 | callback = push_compose_new 89 | (push_talloc_asprintf(context, "%s.compose", name), 90 | context, parser, dup, par); 91 | 92 | /* 93 | * Because of NULL propagation, we only have to check the last 94 | * result to see if everything was created okay. 95 | */ 96 | 97 | if (callback == NULL) goto error; 98 | return callback; 99 | 100 | error: 101 | /* 102 | * Before returning, free any objects we created before the error. 103 | */ 104 | 105 | push_talloc_free(context); 106 | return NULL; 107 | } 108 | -------------------------------------------------------------------------------- /tests/test-noop.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | /*----------------------------------------------------------------------- 25 | * Sample data 26 | */ 27 | 28 | uint32_t INT_1 = 1; 29 | 30 | const uint32_t DATA_01[] = { 1, 2, 3, 4, 5 }; 31 | const size_t LENGTH_01 = 5 * sizeof(uint32_t); 32 | 33 | 34 | /*----------------------------------------------------------------------- 35 | * Test cases 36 | */ 37 | 38 | 39 | START_TEST(test_noop_01) 40 | { 41 | push_parser_t *parser; 42 | push_callback_t *callback; 43 | uint32_t *result; 44 | 45 | PUSH_DEBUG_MSG("---\nStarting test_noop_01\n"); 46 | 47 | /* 48 | * Here, we only present one integer, so it should pass. 49 | */ 50 | 51 | parser = push_parser_new(); 52 | fail_if(parser == NULL, 53 | "Could not allocate a new push parser"); 54 | 55 | callback = push_noop_new("noop", NULL, parser); 56 | fail_if(callback == NULL, 57 | "Could not allocate a new noop callback"); 58 | 59 | push_parser_set_callback(parser, callback); 60 | 61 | fail_unless(push_parser_activate(parser, &INT_1) 62 | == PUSH_SUCCESS, 63 | "Could not activate parser"); 64 | 65 | fail_unless(push_parser_submit_data 66 | (parser, &DATA_01, 1 * sizeof(uint32_t)) 67 | == PUSH_SUCCESS, 68 | "Could not parse data"); 69 | 70 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 71 | "Shouldn't get parse error at noop"); 72 | 73 | result = push_parser_result(parser, uint32_t); 74 | 75 | fail_unless(*result == 1, 76 | "Int doesn't match (got %"PRIu32 77 | ", expected %"PRIu32")", 78 | *result, 1); 79 | 80 | push_parser_free(parser); 81 | } 82 | END_TEST 83 | 84 | 85 | /*----------------------------------------------------------------------- 86 | * Testing harness 87 | */ 88 | 89 | Suite * 90 | test_suite() 91 | { 92 | Suite *s = suite_create("noop"); 93 | 94 | TCase *tc = tcase_create("noop"); 95 | tcase_add_test(tc, test_noop_01); 96 | suite_add_tcase(s, tc); 97 | 98 | return s; 99 | } 100 | 101 | 102 | int 103 | main(int argc, const char **argv) 104 | { 105 | int number_failed; 106 | Suite *suite = test_suite(); 107 | SRunner *runner = srunner_create(suite); 108 | 109 | srunner_run_all(runner, CK_NORMAL); 110 | number_failed = srunner_ntests_failed(runner); 111 | srunner_free(runner); 112 | 113 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 114 | } 115 | -------------------------------------------------------------------------------- /include/push/pairs.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PAIRS_H 12 | #define PUSH_PAIRS_H 13 | 14 | #include 15 | 16 | #include 17 | 18 | 19 | /** 20 | * Stores a pair of values. 21 | */ 22 | 23 | typedef struct _push_pair 24 | { 25 | /** 26 | * The first element of the pair. 27 | */ 28 | 29 | void *first; 30 | 31 | /** 32 | * The second element of the pair. 33 | */ 34 | 35 | void *second; 36 | 37 | } push_pair_t; 38 | 39 | 40 | /** 41 | * Create a new callback that takes a pair as input, and applies 42 | * another callback to the first element of the pair. The second 43 | * element is left unchanged. 44 | * 45 | * This combinator is equivalent to the Haskell first 46 | * arrow operator. 47 | */ 48 | 49 | push_callback_t * 50 | push_first_new(const char *name, 51 | void *parent, 52 | push_parser_t *parser, 53 | push_callback_t *wrapped); 54 | 55 | 56 | /** 57 | * Create a new callback that takes a pair as input, and applies 58 | * another callback to the second element of the pair. The first 59 | * element is left unchanged. 60 | * 61 | * This combinator is equivalent to the Haskell second 62 | * arrow operator. 63 | */ 64 | 65 | push_callback_t * 66 | push_second_new(const char *name, 67 | void *parent, 68 | push_parser_t *parser, 69 | push_callback_t *wrapped); 70 | 71 | 72 | /** 73 | * Create a new callback that duplicates its input. The output will 74 | * be a pair, where the first and second element are both the same as 75 | * the input. 76 | */ 77 | 78 | push_callback_t * 79 | push_dup_new(const char *name, 80 | void *parent, 81 | push_parser_t *parser); 82 | 83 | 84 | /** 85 | * Create a new callback that takes a pair as input, and applies one 86 | * callback to the first element of the pair, and a different callback 87 | * to the second element. 88 | * 89 | * This combinator is equivalent to the Haskell *** arrow 90 | * operator. 91 | */ 92 | 93 | push_callback_t * 94 | push_par_new(const char *name, 95 | void *parent, 96 | push_parser_t *parser, 97 | push_callback_t *first, 98 | push_callback_t *second); 99 | 100 | 101 | /** 102 | * Create a new callback that takes a value as input, and applies two 103 | * callback to the value. The results of the two callbacks are placed 104 | * into a pair, which is the result of the outer callback. 105 | * 106 | * This combinator is equivalent to the Haskell 107 | * &&& arrow operator. 108 | */ 109 | 110 | push_callback_t * 111 | push_both_new(const char *name, 112 | void *parent, 113 | push_parser_t *parser, 114 | push_callback_t *first, 115 | push_callback_t *second); 116 | 117 | 118 | #endif /* PUSH_PAIRS_H */ 119 | -------------------------------------------------------------------------------- /include/push/protobuf/basics.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PROTOBUF_BASICS_H 12 | #define PUSH_PROTOBUF_BASICS_H 13 | 14 | #include 15 | 16 | /** 17 | * The tag portion of a Protocol Buffer field. Consists of a tag 18 | * number (represented by push_protobuf_tag_number_t) and a tag 19 | * type (represented by push_protobuf_tag_type_t). 20 | */ 21 | 22 | typedef uint32_t push_protobuf_tag_t; 23 | 24 | /** 25 | * The field ID portion of a Protocol Buffer tag. 26 | */ 27 | 28 | typedef uint32_t push_protobuf_tag_number_t; 29 | 30 | /** 31 | * The field type portion of a Protocol Buffer tag. 32 | */ 33 | 34 | typedef enum _push_protobuf_tag_type 35 | { 36 | PUSH_PROTOBUF_TAG_TYPE_VARINT = 0, 37 | PUSH_PROTOBUF_TAG_TYPE_FIXED64 = 1, 38 | PUSH_PROTOBUF_TAG_TYPE_LENGTH_DELIMITED = 2, 39 | PUSH_PROTOBUF_TAG_TYPE_START_GROUP = 3, 40 | PUSH_PROTOBUF_TAG_TYPE_END_GROUP = 4, 41 | PUSH_PROTOBUF_TAG_TYPE_FIXED32 = 5 42 | } push_protobuf_tag_type_t; 43 | 44 | 45 | /** 46 | * Construct a push_protobuf_tag_t from a push_protobuf_tag_number_t 47 | * and a push_protobuf_tag_type_t. 48 | */ 49 | 50 | #define PUSH_PROTOBUF_MAKE_TAG(field_number, tag_type) \ 51 | (((field_number) << 3) | ((tag_type) & 0x07)) 52 | 53 | 54 | /** 55 | * Extract the tag type from a push_protobuf_tag_t. 56 | */ 57 | 58 | #define PUSH_PROTOBUF_GET_TAG_TYPE(tag) ((tag) & 0x07) 59 | 60 | 61 | /** 62 | * Extract the tag number from a push_protobuf_tag_t. 63 | */ 64 | 65 | #define PUSH_PROTOBUF_GET_TAG_NUMBER(tag) ((tag) >> 3) 66 | 67 | 68 | /** 69 | * The maximum length of a varint-encoded integer. 70 | */ 71 | 72 | #define PUSH_PROTOBUF_MAX_VARINT_LENGTH 10 73 | 74 | 75 | /** 76 | * The maximum length of a varint-encoded integer whose value fits 77 | * into 32 bits. 78 | */ 79 | 80 | #define PUSH_PROTOBUF_MAX_VARINT32_LENGTH 5 81 | 82 | 83 | /** 84 | * Encode an int32_t into a varint-appropriate uint32_t using the 85 | * Protocol Buffer zig-zag encoding. This ensures that numbers with 86 | * small magnitude have smaller serializations. 87 | */ 88 | 89 | #define PUSH_PROTOBUF_ZIGZAG_ENCODE32(n) \ 90 | (((n) << 1) ^ ((n) >> 31)) 91 | 92 | 93 | /** 94 | * Decode a varint uint32_t into an int32_t using the Protocol Buffer 95 | * zig-zag encoding. 96 | */ 97 | 98 | #define PUSH_PROTOBUF_ZIGZAG_DECODE32(n) \ 99 | (((n) >> 1) ^ -((int32_t) (n) & 1)) 100 | 101 | 102 | /** 103 | * Encode an int64_t into a varint-appropriate uint64_t using the 104 | * Protocol Buffer zig-zag encoding. This ensures that numbers with 105 | * small magnitude have smaller serializations. 106 | */ 107 | 108 | #define PUSH_PROTOBUF_ZIGZAG_ENCODE64(n) \ 109 | (((n) << 1) ^ ((n) >> 63)) 110 | 111 | 112 | /** 113 | * Decode a varint uint64_t into an int64_t using the Protocol Buffer 114 | * zig-zag encoding. 115 | */ 116 | 117 | #define PUSH_PROTOBUF_ZIGZAG_DECODE64(n) \ 118 | (((n) >> 1) ^ -((int64_t) (n) & 1)) 119 | 120 | 121 | #endif /* PUSH_PROTOBUF_BASICS_H */ 122 | -------------------------------------------------------------------------------- /src/eof.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | /** 17 | * The user data struct for an EOF callback. 18 | */ 19 | 20 | typedef struct _eof 21 | { 22 | /** 23 | * The push_callback_t superclass for this callback. 24 | */ 25 | 26 | push_callback_t callback; 27 | 28 | /** 29 | * The continue continuation that will resume the EOF parser. 30 | */ 31 | 32 | push_continue_continuation_t cont; 33 | 34 | /** 35 | * A copy of the input pointer. If our parse succeeds, we use it 36 | * as our output, as well. 37 | */ 38 | 39 | void *input; 40 | 41 | } eof_t; 42 | 43 | 44 | static void 45 | eof_activate(void *user_data, 46 | void *result, 47 | const void *buf, 48 | size_t bytes_remaining) 49 | { 50 | eof_t *eof = (eof_t *) user_data; 51 | 52 | PUSH_DEBUG_MSG("%s: Activating.\n", 53 | push_talloc_get_name(eof)); 54 | eof->input = result; 55 | 56 | /* 57 | * Any data results in a parse error. 58 | */ 59 | 60 | if (bytes_remaining > 0) 61 | { 62 | PUSH_DEBUG_MSG("%s: Expected EOF, but got %zu bytes.\n", 63 | push_talloc_get_name(eof), 64 | bytes_remaining); 65 | 66 | push_continuation_call(eof->callback.error, 67 | PUSH_PARSE_ERROR, 68 | "Expected EOF, but got data"); 69 | 70 | return; 71 | } 72 | 73 | /* 74 | * Otherwise, we need to register a continue continuation. 75 | */ 76 | 77 | push_continuation_call(eof->callback.incomplete, 78 | &eof->cont); 79 | } 80 | 81 | 82 | static void 83 | eof_continue(void *user_data, 84 | const void *buf, 85 | size_t bytes_remaining) 86 | { 87 | eof_t *eof = (eof_t *) user_data; 88 | 89 | /* 90 | * Any data results in a parse error. 91 | */ 92 | 93 | if (bytes_remaining > 0) 94 | { 95 | PUSH_DEBUG_MSG("%s: Expected EOF, but got %zu bytes.\n", 96 | push_talloc_get_name(eof), 97 | bytes_remaining); 98 | 99 | push_continuation_call(eof->callback.error, 100 | PUSH_PARSE_ERROR, 101 | "Expected EOF, but got data"); 102 | 103 | return; 104 | } else { 105 | PUSH_DEBUG_MSG("%s: Reached expected EOF.\n", 106 | push_talloc_get_name(eof)); 107 | 108 | push_continuation_call(eof->callback.success, 109 | eof->input, 110 | buf, bytes_remaining); 111 | 112 | return; 113 | } 114 | } 115 | 116 | 117 | push_callback_t * 118 | push_eof_new(const char *name, 119 | void *parent, 120 | push_parser_t *parser) 121 | { 122 | eof_t *eof = push_talloc(parent, eof_t); 123 | 124 | if (eof == NULL) 125 | return NULL; 126 | 127 | if (name == NULL) name = "eof"; 128 | push_talloc_set_name_const(eof, name); 129 | 130 | push_callback_init(&eof->callback, parser, eof, 131 | eof_activate, 132 | NULL, NULL, NULL); 133 | 134 | /* 135 | * Fill in the continuation objects for the continuations that we 136 | * implement. 137 | */ 138 | 139 | push_continuation_set(&eof->cont, 140 | eof_continue, 141 | eof); 142 | 143 | return &eof->callback; 144 | } 145 | -------------------------------------------------------------------------------- /src/protobuf/hwm-string.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | push_callback_t * 24 | push_protobuf_hwm_string_new(const char *name, 25 | void *parent, 26 | push_parser_t *parser, 27 | hwm_buffer_t *buf) 28 | { 29 | void *context; 30 | push_callback_t *read_size = NULL; 31 | push_callback_t *read = NULL; 32 | push_callback_t *compose = NULL; 33 | 34 | /* 35 | * Create a memory context for the objects we're about to create. 36 | */ 37 | 38 | context = push_talloc_new(parent); 39 | if (context == NULL) return NULL; 40 | 41 | /* 42 | * Create the callbacks. 43 | */ 44 | 45 | if (name == NULL) name = "pb-hwm-string"; 46 | 47 | read_size = push_protobuf_varint_size_new 48 | (push_talloc_asprintf(context, "%s.size", name), 49 | context, parser); 50 | read = push_hwm_string_new 51 | (push_talloc_asprintf(context, "%s.read", name), 52 | context, parser, buf); 53 | compose = push_compose_new 54 | (push_talloc_asprintf(context, "%s.compose", name), 55 | context, parser, read_size, read); 56 | 57 | /* 58 | * Because of NULL propagation, we only have to check the last 59 | * result to see if everything was created okay. 60 | */ 61 | 62 | if (compose == NULL) goto error; 63 | return compose; 64 | 65 | error: 66 | /* 67 | * Before returning, free any objects we created before the error. 68 | */ 69 | 70 | push_talloc_free(context); 71 | return NULL; 72 | } 73 | 74 | 75 | bool 76 | push_protobuf_add_hwm_string(const char *message_name, 77 | const char *field_name, 78 | void *parent, 79 | push_parser_t *parser, 80 | push_protobuf_field_map_t *field_map, 81 | push_protobuf_tag_number_t field_number, 82 | hwm_buffer_t *dest) 83 | { 84 | void *context; 85 | const char *full_field_name; 86 | push_callback_t *field_callback; 87 | 88 | /* 89 | * If the field map is NULL, return false. 90 | */ 91 | 92 | if (field_map == NULL) 93 | return false; 94 | 95 | /* 96 | * Create a memory context for the objects we're about to create. 97 | */ 98 | 99 | context = push_talloc_new(parent); 100 | if (context == NULL) return NULL; 101 | 102 | /* 103 | * Create the callbacks. 104 | */ 105 | 106 | if (message_name == NULL) message_name = "message"; 107 | if (field_name == NULL) field_name = ".hwm"; 108 | 109 | full_field_name = 110 | push_talloc_asprintf(context, "%s.%s", 111 | message_name, field_name); 112 | 113 | field_callback = 114 | push_protobuf_hwm_string_new 115 | (full_field_name, 116 | context, parser, dest); 117 | if (field_callback == NULL) goto error; 118 | 119 | /* 120 | * Try to add the new field. If we can't, free the callback 121 | * before returning. 122 | */ 123 | 124 | if (!push_protobuf_field_map_add_field 125 | (full_field_name, 126 | parser, field_map, field_number, 127 | PUSH_PROTOBUF_TAG_TYPE_LENGTH_DELIMITED, 128 | field_callback)) 129 | { 130 | goto error; 131 | } 132 | 133 | return true; 134 | 135 | error: 136 | /* 137 | * Before returning, free any objects we created before the error. 138 | */ 139 | 140 | push_talloc_free(context); 141 | return false; 142 | } 143 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from checklib import * 4 | 5 | vars = Variables('.scons.vars', ARGUMENTS) 6 | 7 | vars.AddVariables( 8 | PathVariable("prefix", "Installation prefix", "/usr", 9 | PathVariable.PathAccept), 10 | ('AR', "The static archiver to use"), 11 | ('RANLIB', "The archive indexer to use"), 12 | ('CC', "The C compiler to use"), 13 | ('LINK', "The linker to use"), 14 | ('SHLINK', "The shared library linker to use"), 15 | ('CCFLAGS', "Any additional options to pass in to the C compiler"), 16 | ('CPPFLAGS', "Any additional options to pass in to the C preprocessor"), 17 | ('LINKFLAGS', "Any additional options to pass in to the linker"), 18 | ('SHLINKFLAGS', "Any additional options to pass in to the shared library linker"), 19 | ) 20 | 21 | 22 | root_env = Environment(tools=['default', 'packaging'], 23 | package="libpush", 24 | pkg_version="1.0-dev", 25 | CCFLAGS="-O2", 26 | BINDIR = "$prefix/bin", 27 | DOCDIR = "$prefix/share/doc/$package", 28 | LIBDIR = "$prefix/lib", 29 | INCLUDEDIR = "$prefix/include") 30 | 31 | vars.Update(root_env) 32 | vars.Save(".scons.vars", root_env) 33 | 34 | root_env.MergeFlags('-g -Wall -Werror') 35 | 36 | # An action that can clean up the scons temporary files. 37 | 38 | if 'sdist' not in COMMAND_LINE_TARGETS: 39 | root_env.Clean("distclean", 40 | [ 41 | ".sconf_temp", 42 | ".sconsign.dblite", 43 | ".scons.vars", 44 | ".scons.vars.check", 45 | "config.log", 46 | ]) 47 | 48 | # Only run the configuration steps if we're actually going to build 49 | # something. 50 | 51 | if not GetOption('clean') and not GetOption('help'): 52 | conf = root_env.Configure(custom_tests= 53 | { 54 | 'CheckLibInPath': CheckLibInPath, 55 | }) 56 | 57 | if not conf.CheckCC(): 58 | print "!! Your compiler and/or environment is not properly configured." 59 | Exit(0) 60 | 61 | 62 | if not conf.CheckLibInPath("check", 63 | library="check", 64 | call="", 65 | header="#include "): 66 | print "!! Cannot find the check library." 67 | Exit(0) 68 | 69 | 70 | if not conf.CheckLibInPath("libhwm", 71 | library="hwm", 72 | call="hwm_buffer_new()", 73 | header="#include "): 74 | print "!! Cannot find the libhwm library." 75 | Exit(0) 76 | 77 | 78 | root_env = conf.Finish() 79 | 80 | # Set up a list of source files for the packaging target later on. 81 | # Each SConscript file is responsible for updating this list. 82 | 83 | SOURCE_FILES = [] 84 | Export('SOURCE_FILES') 85 | 86 | # Add the root scons files to the SOURCES_LIST. 87 | 88 | build_files = map(File, \ 89 | [ 90 | "SConstruct", 91 | "site_scons/checklib.py", 92 | ]) 93 | 94 | SOURCE_FILES.extend(build_files) 95 | 96 | # Include the subdirectory SConscripts 97 | 98 | Export('root_env') 99 | SConscript([ 100 | 'doc/SConscript', 101 | 'include/SConscript', 102 | 'src/SConscript', 103 | 'tests/SConscript', 104 | ]) 105 | 106 | # Install documentation files 107 | 108 | doc_files = map(File, \ 109 | [ 110 | "LICENSE.txt", 111 | ]) 112 | 113 | SOURCE_FILES.extend(doc_files) 114 | 115 | root_env.Alias("install", root_env.Install("$DOCDIR", doc_files)) 116 | 117 | # Define packaging targets 118 | 119 | license = 'BSD' 120 | summary = 'A push parsing framework' 121 | source_url = 'http://github.com/dcreager/libpush.git' 122 | 123 | src = root_env.Package(NAME="$package", 124 | VERSION="${pkg_version}", 125 | PACKAGETYPE='src_targz', 126 | source=SOURCE_FILES) 127 | root_env.Alias("sdist", src) 128 | 129 | if GetOption('clean'): 130 | root_env.Default("sdist") 131 | root_env.Clean("sdist", "$package-${pkg_version}") 132 | -------------------------------------------------------------------------------- /src/compose.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | /** 20 | * The user data struct for a compose callback. 21 | */ 22 | 23 | typedef struct _compose 24 | { 25 | /** 26 | * The push_callback_t superclass for this callback. 27 | */ 28 | 29 | push_callback_t callback; 30 | 31 | /** 32 | * The first wrapped callback in the composition. 33 | */ 34 | 35 | push_callback_t *first; 36 | 37 | /** 38 | * The second wrapped callback in the composition. 39 | */ 40 | 41 | push_callback_t *second; 42 | 43 | } compose_t; 44 | 45 | 46 | static void 47 | compose_set_success(void *user_data, 48 | push_success_continuation_t *success) 49 | { 50 | compose_t *compose = (compose_t *) user_data; 51 | 52 | /* 53 | * The second wrapped callback should succeed using this new 54 | * continuation. The first callback still succeeds by activating 55 | * the second. 56 | */ 57 | 58 | push_continuation_call(&compose->second->set_success, 59 | success); 60 | } 61 | 62 | 63 | static void 64 | compose_set_incomplete(void *user_data, 65 | push_incomplete_continuation_t *incomplete) 66 | { 67 | compose_t *compose = (compose_t *) user_data; 68 | 69 | /* 70 | * Both wrapped callbacks should use this new incomplete 71 | * continuation. 72 | */ 73 | 74 | push_continuation_call(&compose->first->set_incomplete, 75 | incomplete); 76 | push_continuation_call(&compose->second->set_incomplete, 77 | incomplete); 78 | } 79 | 80 | 81 | static void 82 | compose_set_error(void *user_data, 83 | push_error_continuation_t *error) 84 | { 85 | compose_t *compose = (compose_t *) user_data; 86 | 87 | /* 88 | * Both wrapped callbacks should use this new error continuation. 89 | */ 90 | 91 | push_continuation_call(&compose->first->set_error, 92 | error); 93 | push_continuation_call(&compose->second->set_error, 94 | error); 95 | } 96 | 97 | 98 | push_callback_t * 99 | push_compose_new(const char *name, 100 | void *parent, 101 | push_parser_t *parser, 102 | push_callback_t *first, 103 | push_callback_t *second) 104 | { 105 | compose_t *compose; 106 | 107 | /* 108 | * If either wrapped callback is NULL, return NULL ourselves. 109 | */ 110 | 111 | if ((first == NULL) || (second == NULL)) 112 | return NULL; 113 | 114 | /* 115 | * Allocate the user data struct. 116 | */ 117 | 118 | compose = push_talloc(parent, compose_t); 119 | if (compose == NULL) return NULL; 120 | 121 | /* 122 | * Make the wrapped callbacks children of the new callback. 123 | */ 124 | 125 | push_talloc_steal(compose, first); 126 | push_talloc_steal(compose, second); 127 | 128 | /* 129 | * Fill in the data items. 130 | */ 131 | 132 | compose->first = first; 133 | compose->second = second; 134 | 135 | /* 136 | * Initialize the push_callback_t instance. 137 | */ 138 | 139 | if (name == NULL) name = "compose"; 140 | push_talloc_set_name_const(compose, name); 141 | 142 | push_callback_init(&compose->callback, parser, compose, 143 | NULL, 144 | compose_set_success, 145 | compose_set_incomplete, 146 | compose_set_error); 147 | 148 | /* 149 | * The compose should activate by activating the first wrapped 150 | * callback. 151 | */ 152 | 153 | compose->callback.activate = compose->first->activate; 154 | 155 | /* 156 | * The first callback should succeed by activating the second. 157 | */ 158 | 159 | push_continuation_call(&first->set_success, 160 | &second->activate); 161 | 162 | return &compose->callback; 163 | } 164 | -------------------------------------------------------------------------------- /include/push/combinators.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_COMBINATORS_H 12 | #define PUSH_COMBINATORS_H 13 | 14 | /** 15 | * @file 16 | * 17 | * This file defines the parser combinators, which wrap other 18 | * combinators. 19 | */ 20 | 21 | 22 | /** 23 | * Create a new callback that composes together two child callbacks. 24 | * The first callback is allowed to parse the data until it finishes, 25 | * by returning a success or error code. (PUSH_INCOMPLETE doesn't 26 | * count as finishing the parse.) Once the first callback has 27 | * finished, its result is used to activate the second callback, at 28 | * which point it is allowed to parse the remaining data. Once the 29 | * second callback finishes, the push_compose_t finishes. 30 | * 31 | * This combinator is equivalent to the Haskell 32 | * >>> arrow operator. 33 | */ 34 | 35 | push_callback_t * 36 | push_compose_new(const char *name, 37 | void *parent, 38 | push_parser_t *parser, 39 | push_callback_t *first, 40 | push_callback_t *second); 41 | 42 | 43 | /** 44 | * Create a new callback that calls another callback repeatedly. The 45 | * input of each iteration of the wrapped callback is passed as input 46 | * into the next iteration. We terminate the loop when the wrapped 47 | * callback generates a parse failure; whatever result we had 48 | * accumulated to that point is then returned as the result of the 49 | * fold callback. Note, however, that the wrapped callback must 50 | * generate the parse error immediately; if it partially parses 51 | * the data, and then discovers the parse error in a later call, we 52 | * cannot backtrack the data. This case generates a parse error for 53 | * the fold. 54 | */ 55 | 56 | push_callback_t * 57 | push_fold_new(const char *name, 58 | void *parent, 59 | push_parser_t *parser, 60 | push_callback_t *wrapped); 61 | 62 | 63 | /** 64 | * Create a new callback that wraps another callback, ensuring that a 65 | * certain number of bytes are available before calling the wrapped 66 | * callback. The data is buffered, if needed, until the minimum is 67 | * met. 68 | */ 69 | 70 | push_callback_t * 71 | push_min_bytes_new(const char *name, 72 | void *parent, 73 | push_parser_t *parser, 74 | push_callback_t *wrapped, 75 | size_t minimum_bytes); 76 | 77 | 78 | /** 79 | * Create a new callback that wraps another callback, ensuring that no 80 | * more than a certain number of bytes are passed into the wrapped 81 | * callback. There is no guarantee that exactly this many 82 | * bytes will be processed; if the wrapped callback returns a success 83 | * code earlier, the max-bytes callback succeeds, too. Once we reach 84 | * the maximum number of bytes, we send an EOF to the wrapped callback 85 | * to give it a chance to throw a parse error, if necessary. 86 | */ 87 | 88 | push_callback_t * 89 | push_max_bytes_new(const char *name, 90 | void *parent, 91 | push_parser_t *parser, 92 | push_callback_t *wrapped, 93 | size_t maximum_bytes); 94 | 95 | 96 | /** 97 | * Create a new callback that wraps another callback, ensuring that no 98 | * more than a certain number of bytes are passed into the wrapped 99 | * callback. This works exactly like the max-bytes combinator, except 100 | * that the number of bytes is taken as an input during parsing, 101 | * rather than provided once when constructing the callback. The 102 | * input to the callback should be a pair. The first element should 103 | * be a pointer to a size_t, indicating the number of bytes to use as 104 | * a threshold. The second element is the input value that should be 105 | * passed into the wrapped callback. 106 | */ 107 | 108 | push_callback_t * 109 | push_dynamic_max_bytes_new(const char *name, 110 | void *parent, 111 | push_parser_t *parser, 112 | push_callback_t *wrapped); 113 | 114 | 115 | 116 | #endif /* PUSH_COMBINATORS_H */ 117 | -------------------------------------------------------------------------------- /tests/test-eof.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | 26 | /*----------------------------------------------------------------------- 27 | * Sample data 28 | */ 29 | 30 | const uint32_t DATA_01[] = { 1, 2, 3, 4, 5 }; 31 | const size_t LENGTH_01 = 5 * sizeof(uint32_t); 32 | 33 | 34 | /*----------------------------------------------------------------------- 35 | * Test cases 36 | */ 37 | 38 | 39 | START_TEST(test_eof_01) 40 | { 41 | push_parser_t *parser; 42 | push_callback_t *integer; 43 | push_callback_t *eof; 44 | push_callback_t *callback; 45 | uint32_t *result; 46 | 47 | PUSH_DEBUG_MSG("---\nStarting test_eof_01\n"); 48 | 49 | /* 50 | * Here, we only present one integer, so it should pass. 51 | */ 52 | 53 | parser = push_parser_new(); 54 | fail_if(parser == NULL, 55 | "Could not allocate a new push parser"); 56 | 57 | integer = integer_callback_new("integer", NULL, parser); 58 | fail_if(integer == NULL, 59 | "Could not allocate a new int callback"); 60 | 61 | eof = push_eof_new("eof", NULL, parser); 62 | fail_if(eof == NULL, 63 | "Could not allocate a new EOF callback"); 64 | 65 | callback = push_compose_new("compose", NULL, parser, integer, eof); 66 | fail_if(callback == NULL, 67 | "Could not allocate a new compose callback"); 68 | 69 | push_parser_set_callback(parser, callback); 70 | 71 | fail_unless(push_parser_activate(parser, NULL) 72 | == PUSH_INCOMPLETE, 73 | "Could not parse data"); 74 | 75 | fail_unless(push_parser_submit_data 76 | (parser, &DATA_01, 1 * sizeof(uint32_t)) 77 | == PUSH_INCOMPLETE, 78 | "Could not parse data"); 79 | 80 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 81 | "Shouldn't get parse error at EOF"); 82 | 83 | result = push_parser_result(parser, uint32_t); 84 | 85 | fail_unless(*result == 1, 86 | "Int doesn't match (got %"PRIu32 87 | ", expected %"PRIu32")", 88 | *result, 1); 89 | 90 | push_parser_free(parser); 91 | } 92 | END_TEST 93 | 94 | 95 | START_TEST(test_parse_error_01) 96 | { 97 | push_parser_t *parser; 98 | push_callback_t *integer; 99 | push_callback_t *eof; 100 | push_callback_t *callback; 101 | 102 | PUSH_DEBUG_MSG("---\nStarting test_parse_error_01\n"); 103 | 104 | /* 105 | * Here, we present two integers, so we should get a parse error. 106 | */ 107 | 108 | parser = push_parser_new(); 109 | fail_if(parser == NULL, 110 | "Could not allocate a new push parser"); 111 | 112 | integer = integer_callback_new("integer", NULL, parser); 113 | fail_if(integer == NULL, 114 | "Could not allocate a new int callback"); 115 | 116 | eof = push_eof_new("eof", NULL, parser); 117 | fail_if(eof == NULL, 118 | "Could not allocate a new EOF callback"); 119 | 120 | callback = push_compose_new("compose", NULL, parser, integer, eof); 121 | fail_if(callback == NULL, 122 | "Could not allocate a new compose callback"); 123 | 124 | push_parser_set_callback(parser, callback); 125 | 126 | fail_unless(push_parser_activate(parser, NULL) 127 | == PUSH_INCOMPLETE, 128 | "Could not parse data"); 129 | 130 | fail_unless(push_parser_submit_data 131 | (parser, &DATA_01, 2 * sizeof(uint32_t)) 132 | == PUSH_PARSE_ERROR, 133 | "Should get a parse error with extra data"); 134 | 135 | push_parser_free(parser); 136 | } 137 | END_TEST 138 | 139 | 140 | /*----------------------------------------------------------------------- 141 | * Testing harness 142 | */ 143 | 144 | Suite * 145 | test_suite() 146 | { 147 | Suite *s = suite_create("eof"); 148 | 149 | TCase *tc = tcase_create("eof"); 150 | tcase_add_test(tc, test_eof_01); 151 | tcase_add_test(tc, test_parse_error_01); 152 | suite_add_tcase(s, tc); 153 | 154 | return s; 155 | } 156 | 157 | 158 | int 159 | main(int argc, const char **argv) 160 | { 161 | int number_failed; 162 | Suite *suite = test_suite(); 163 | SRunner *runner = srunner_create(suite); 164 | 165 | srunner_run_all(runner, CK_NORMAL); 166 | number_failed = srunner_ntests_failed(runner); 167 | srunner_free(runner); 168 | 169 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 170 | } 171 | -------------------------------------------------------------------------------- /tests/sum-callback.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | /*----------------------------------------------------------------------- 24 | * Sum callback implementation 25 | * 26 | * The sum callback is implemented using pairs: It expects to receive 27 | * a pair of uint32_t's. The first one is the integer that was just 28 | * parsed; the second is the previous value of the sum. The output is 29 | * a pair, with the first element NULL, and the second the new sum. 30 | * 31 | * The “repeated sum” callback composes an integer callback and a sum 32 | * callback together. The composed callback has the following design: 33 | * 34 | * +------------------------------------------------------+ 35 | * | next | 36 | * | ignore +-----+ int +-------+ | 37 | * | +-----+ /===>| Int |=======>| | | 38 | * |==>| Dup |==< +-----+ | Inner |===========>| 39 | * | +-----+ \ | Sum | new sum | 40 | * | \=================>| | | 41 | * | old sum +-------+ | 42 | * | | 43 | * +------------------------------------------------------+ 44 | * 45 | * So it takes in a pair, but the callback expects the first element 46 | * to be NULL on input, and outputs a NULL there as well. The first 47 | * element is only used internally in between the Int and Sum 48 | * callbacks. 49 | */ 50 | 51 | 52 | static bool 53 | inner_sum_func(uint32_t *result, push_pair_t *input, uint32_t **output) 54 | { 55 | uint32_t *input_int = (uint32_t *) input->first; 56 | uint32_t *input_sum = (uint32_t *) input->second; 57 | 58 | PUSH_DEBUG_MSG("inner-sum: Activating callback. " 59 | "Received value %"PRIu32", sum %"PRIu32".\n", 60 | *input_int, 61 | *input_sum); 62 | 63 | *result = *input_int + *input_sum; 64 | 65 | PUSH_DEBUG_MSG("inner-sum: Adding, sum is now %"PRIu32"\n", 66 | *result); 67 | 68 | *output = result; 69 | return true; 70 | } 71 | 72 | 73 | push_define_pure_data_callback(inner_sum_new, inner_sum_func, 74 | "inner-sum", 75 | push_pair_t, uint32_t, uint32_t); 76 | 77 | 78 | static push_callback_t * 79 | inner_sum_callback_new(const char *name, 80 | void *parent, 81 | push_parser_t *parser) 82 | { 83 | return inner_sum_new(name, parent, parser, NULL); 84 | } 85 | 86 | 87 | push_callback_t * 88 | sum_callback_new(const char *name, 89 | void *parent, 90 | push_parser_t *parser) 91 | { 92 | void *context; 93 | push_callback_t *dup; 94 | push_callback_t *integer; 95 | push_callback_t *first; 96 | push_callback_t *inner_sum; 97 | push_callback_t *compose1; 98 | push_callback_t *compose2; 99 | 100 | /* 101 | * Create a memory context for the objects we're about to create. 102 | */ 103 | 104 | context = push_talloc_new(parent); 105 | if (context == NULL) return NULL; 106 | 107 | /* 108 | * Create the callbacks. 109 | */ 110 | 111 | if (name == NULL) name = "sum"; 112 | 113 | dup = push_dup_new 114 | (push_talloc_asprintf(context, "%s.dup", name), 115 | context, parser); 116 | integer = integer_callback_new 117 | (push_talloc_asprintf(context, "%s.integer", name), 118 | context, parser); 119 | first = push_first_new 120 | (push_talloc_asprintf(context, "%s.first", name), 121 | context, parser, integer); 122 | inner_sum = inner_sum_callback_new 123 | (push_talloc_asprintf(context, "%s.inner", name), 124 | context, parser); 125 | compose1 = push_compose_new 126 | (push_talloc_asprintf(context, "%s.compose1", name), 127 | context, parser, dup, first); 128 | compose2 = push_compose_new 129 | (push_talloc_asprintf(context, "%s.compose2", name), 130 | context, parser, compose1, inner_sum); 131 | 132 | /* 133 | * Because of NULL propagation, we only have to check the last 134 | * result to see if everything was created okay. 135 | */ 136 | 137 | if (compose2 == NULL) goto error; 138 | return compose2; 139 | 140 | error: 141 | /* 142 | * Before returning, free any objects we created before the error. 143 | */ 144 | 145 | push_talloc_free(context); 146 | return NULL; 147 | } 148 | -------------------------------------------------------------------------------- /tests/test-skip.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | 23 | /*----------------------------------------------------------------------- 24 | * Sample data 25 | */ 26 | 27 | const uint8_t DATA_01[] = "1234567890"; 28 | const size_t LENGTH_01 = 10; 29 | 30 | 31 | /*----------------------------------------------------------------------- 32 | * Test cases 33 | */ 34 | 35 | 36 | START_TEST(test_skip_01) 37 | { 38 | push_parser_t *parser; 39 | push_callback_t *callback; 40 | size_t bytes_to_skip = 5; 41 | 42 | PUSH_DEBUG_MSG("---\nStarting test_skip_01\n"); 43 | 44 | /* 45 | * Skip over five bytes, and provide 5 bytes. This should 46 | * succeed. 47 | */ 48 | 49 | parser = push_parser_new(); 50 | fail_if(parser == NULL, 51 | "Could not allocate a new push parser"); 52 | 53 | callback = push_skip_new("skip", NULL, parser); 54 | fail_if(callback == NULL, 55 | "Could not allocate a new skip callback"); 56 | 57 | push_parser_set_callback(parser, callback); 58 | 59 | fail_unless(push_parser_activate(parser, &bytes_to_skip) 60 | == PUSH_INCOMPLETE, 61 | "Could not activate parser"); 62 | 63 | fail_unless(push_parser_submit_data 64 | (parser, &DATA_01, 5) == PUSH_SUCCESS, 65 | "Could not parse data"); 66 | 67 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 68 | "Shouldn't get parse error at EOF"); 69 | 70 | push_parser_free(parser); 71 | } 72 | END_TEST 73 | 74 | 75 | START_TEST(test_skip_02) 76 | { 77 | push_parser_t *parser; 78 | push_callback_t *callback; 79 | size_t bytes_to_skip = 5; 80 | 81 | PUSH_DEBUG_MSG("---\nStarting test_skip_02\n"); 82 | 83 | /* 84 | * Skip over five bytes, and provide 7 bytes. This should 85 | * succeed. 86 | */ 87 | 88 | parser = push_parser_new(); 89 | fail_if(parser == NULL, 90 | "Could not allocate a new push parser"); 91 | 92 | callback = push_skip_new("skip", NULL, parser); 93 | fail_if(callback == NULL, 94 | "Could not allocate a new skip callback"); 95 | 96 | push_parser_set_callback(parser, callback); 97 | 98 | fail_unless(push_parser_activate(parser, &bytes_to_skip) 99 | == PUSH_INCOMPLETE, 100 | "Could not activate parser"); 101 | 102 | fail_unless(push_parser_submit_data 103 | (parser, &DATA_01, 7) == PUSH_SUCCESS, 104 | "Could not parse data"); 105 | 106 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 107 | "Shouldn't get parse error at EOF"); 108 | 109 | push_parser_free(parser); 110 | } 111 | END_TEST 112 | 113 | 114 | START_TEST(test_skip_03) 115 | { 116 | push_parser_t *parser; 117 | push_callback_t *callback; 118 | size_t bytes_to_skip = 5; 119 | 120 | PUSH_DEBUG_MSG("---\nStarting test_skip_03\n"); 121 | 122 | /* 123 | * Skip over five bytes, and provide 3 bytes. This should fail. 124 | */ 125 | 126 | parser = push_parser_new(); 127 | fail_if(parser == NULL, 128 | "Could not allocate a new push parser"); 129 | 130 | callback = push_skip_new("skip", NULL, parser); 131 | fail_if(callback == NULL, 132 | "Could not allocate a new skip callback"); 133 | 134 | push_parser_set_callback(parser, callback); 135 | 136 | fail_unless(push_parser_activate(parser, &bytes_to_skip) 137 | == PUSH_INCOMPLETE, 138 | "Could not activate parser"); 139 | 140 | fail_unless(push_parser_submit_data 141 | (parser, &DATA_01, 3) == PUSH_INCOMPLETE, 142 | "Could not parse data"); 143 | 144 | fail_unless(push_parser_eof(parser) == PUSH_PARSE_ERROR, 145 | "Should get parse error at EOF"); 146 | 147 | push_parser_free(parser); 148 | } 149 | END_TEST 150 | 151 | 152 | /*----------------------------------------------------------------------- 153 | * Testing harness 154 | */ 155 | 156 | Suite * 157 | test_suite() 158 | { 159 | Suite *s = suite_create("skip"); 160 | 161 | TCase *tc = tcase_create("skip"); 162 | tcase_add_test(tc, test_skip_01); 163 | tcase_add_test(tc, test_skip_02); 164 | tcase_add_test(tc, test_skip_03); 165 | suite_add_tcase(s, tc); 166 | 167 | return s; 168 | } 169 | 170 | 171 | int 172 | main(int argc, const char **argv) 173 | { 174 | int number_failed; 175 | Suite *suite = test_suite(); 176 | SRunner *runner = srunner_create(suite); 177 | 178 | srunner_run_all(runner, CK_NORMAL); 179 | number_failed = srunner_ntests_failed(runner); 180 | srunner_free(runner); 181 | 182 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 183 | } 184 | -------------------------------------------------------------------------------- /src/callback.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #if PUSH_FREE 18 | #define PUSH_FREE_MSG(...) PUSH_DEBUG_MSG(__VA_ARGS__) 19 | #else 20 | #define PUSH_FREE_MSG(...) /* skipping debug message */ 21 | #endif 22 | 23 | 24 | static void 25 | default_set_success(void *user_data, 26 | push_success_continuation_t *success) 27 | { 28 | push_callback_t *callback = (push_callback_t *) user_data; 29 | callback->success = success; 30 | } 31 | 32 | 33 | static void 34 | default_set_incomplete(void *user_data, 35 | push_incomplete_continuation_t *incomplete) 36 | { 37 | push_callback_t *callback = (push_callback_t *) user_data; 38 | callback->incomplete = incomplete; 39 | } 40 | 41 | 42 | static void 43 | default_set_error(void *user_data, 44 | push_error_continuation_t *error) 45 | { 46 | push_callback_t *callback = (push_callback_t *) user_data; 47 | callback->error = error; 48 | } 49 | 50 | 51 | void 52 | _push_callback_init 53 | (push_callback_t *callback, 54 | push_parser_t *parser, 55 | const char *user_data_name, 56 | void *user_data, 57 | const char *activate_name, 58 | push_success_func_t *activate_func, 59 | const char *set_success_name, 60 | push_set_success_func_t *set_success_func, 61 | const char *set_incomplete_name, 62 | push_set_incomplete_func_t *set_incomplete_func, 63 | const char *set_error_name, 64 | push_set_error_func_t *set_error_func) 65 | { 66 | /* 67 | * Fill in the callback's activate continuation object. 68 | */ 69 | 70 | push_continuation_set(&callback->activate, 71 | activate_func, 72 | user_data); 73 | 74 | #if PUSH_CONTINUATION_DEBUG 75 | /* 76 | * We have to override the name of the continuation functions; the 77 | * default implementation of the push_continuation_set macro will 78 | * always call it “activate_func”, etc. 79 | */ 80 | 81 | callback->activate.name = activate_name; 82 | #endif 83 | 84 | 85 | if (set_success_func == NULL) 86 | { 87 | push_continuation_set(&callback->set_success, 88 | default_set_success, callback); 89 | 90 | #if PUSH_CONTINUATION_DEBUG 91 | { 92 | const char *name = 93 | push_talloc_asprintf(callback, "%s%s", 94 | user_data_name, "_set_success"); 95 | 96 | if (name != NULL) 97 | callback->set_success.name = name; 98 | } 99 | #endif 100 | } else { 101 | push_continuation_set(&callback->set_success, 102 | set_success_func, 103 | user_data); 104 | 105 | #if PUSH_CONTINUATION_DEBUG 106 | callback->set_success.name = set_success_name; 107 | #endif 108 | } 109 | 110 | 111 | if (set_incomplete_func == NULL) 112 | { 113 | push_continuation_set(&callback->set_incomplete, 114 | default_set_incomplete, callback); 115 | 116 | #if PUSH_CONTINUATION_DEBUG 117 | { 118 | const char *name = 119 | push_talloc_asprintf(callback, "%s%s", 120 | user_data_name, "_set_incomplete"); 121 | 122 | if (name != NULL) 123 | callback->set_incomplete.name = name; 124 | } 125 | #endif 126 | } else { 127 | push_continuation_set(&callback->set_incomplete, 128 | set_incomplete_func, 129 | user_data); 130 | 131 | #if PUSH_CONTINUATION_DEBUG 132 | callback->set_incomplete.name = set_incomplete_name; 133 | #endif 134 | } 135 | 136 | 137 | if (set_error_func == NULL) 138 | { 139 | push_continuation_set(&callback->set_error, 140 | default_set_error, callback); 141 | 142 | #if PUSH_CONTINUATION_DEBUG 143 | { 144 | const char *name = 145 | push_talloc_asprintf(callback, "%s%s", 146 | user_data_name, "_set_error"); 147 | 148 | if (name != NULL) 149 | callback->set_error.name = name; 150 | } 151 | #endif 152 | } else { 153 | push_continuation_set(&callback->set_error, 154 | set_error_func, 155 | user_data); 156 | 157 | #if PUSH_CONTINUATION_DEBUG 158 | callback->set_error.name = set_error_name; 159 | #endif 160 | } 161 | 162 | /* 163 | * By default, we call the parser's implementations of the 164 | * continuations that we call. 165 | */ 166 | 167 | push_continuation_call(&callback->set_success, 168 | &parser->success); 169 | 170 | push_continuation_call(&callback->set_incomplete, 171 | &parser->incomplete); 172 | 173 | push_continuation_call(&callback->set_error, 174 | &parser->error); 175 | } 176 | -------------------------------------------------------------------------------- /src/fixed.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | /** 18 | * The user data struct for a fixed callback. 19 | */ 20 | 21 | typedef struct _fixed 22 | { 23 | /** 24 | * The push_callback_t superclass for this callback. 25 | */ 26 | 27 | push_callback_t callback; 28 | 29 | /** 30 | * The continue continuation that will resume the fixed parser. 31 | */ 32 | 33 | push_continue_continuation_t cont; 34 | 35 | /** 36 | * The size of the buffer. 37 | */ 38 | 39 | size_t size; 40 | 41 | } fixed_t; 42 | 43 | 44 | static void 45 | fixed_continue(void *user_data, 46 | const void *buf, 47 | size_t bytes_remaining) 48 | { 49 | fixed_t *fixed = (fixed_t *) user_data; 50 | 51 | PUSH_DEBUG_MSG("%s: Processing %zu bytes at %p.\n", 52 | push_talloc_get_name(fixed), 53 | bytes_remaining, buf); 54 | 55 | if (bytes_remaining < fixed->size) 56 | { 57 | PUSH_DEBUG_MSG("%s: Need more than %zu bytes to read data.\n", 58 | push_talloc_get_name(fixed), 59 | bytes_remaining); 60 | 61 | push_continuation_call(fixed->callback.error, 62 | PUSH_PARSE_ERROR, 63 | "Need more bytes to read data"); 64 | 65 | return; 66 | } else { 67 | void *value = (void *) buf; 68 | 69 | buf += fixed->size; 70 | bytes_remaining -= fixed->size; 71 | 72 | push_continuation_call(fixed->callback.success, 73 | value, 74 | buf, bytes_remaining); 75 | 76 | return; 77 | } 78 | } 79 | 80 | 81 | static void 82 | fixed_activate(void *user_data, 83 | void *result, 84 | const void *buf, 85 | size_t bytes_remaining) 86 | { 87 | fixed_t *fixed = (fixed_t *) user_data; 88 | 89 | if (bytes_remaining == 0) 90 | { 91 | /* 92 | * If we don't get any data when we're activated, return an 93 | * incomplete and wait for some data. 94 | */ 95 | 96 | push_continuation_call(fixed->callback.incomplete, 97 | &fixed->cont); 98 | 99 | return; 100 | 101 | } else { 102 | /* 103 | * Otherwise let the continue continuation go ahead and 104 | * process this chunk of data. 105 | */ 106 | 107 | fixed_continue(user_data, buf, bytes_remaining); 108 | return; 109 | } 110 | } 111 | 112 | 113 | static push_callback_t * 114 | inner_fixed_new(const char *name, 115 | void *parent, 116 | push_parser_t *parser, 117 | size_t size) 118 | { 119 | fixed_t *fixed = push_talloc(parent, fixed_t); 120 | 121 | if (fixed == NULL) 122 | return NULL; 123 | 124 | /* 125 | * Fill in the data items. 126 | */ 127 | 128 | fixed->size = size; 129 | 130 | /* 131 | * Initialize the push_callback_t instance. 132 | */ 133 | 134 | if (name == NULL) name = "fixed"; 135 | push_talloc_set_name_const(fixed, name); 136 | 137 | push_callback_init(&fixed->callback, parser, fixed, 138 | fixed_activate, 139 | NULL, NULL, NULL); 140 | 141 | /* 142 | * Fill in the continuation objects for the continuations that we 143 | * implement. 144 | */ 145 | 146 | push_continuation_set(&fixed->cont, 147 | fixed_continue, 148 | fixed); 149 | 150 | return &fixed->callback; 151 | } 152 | 153 | 154 | push_callback_t * 155 | push_fixed_new(const char *name, 156 | void *parent, 157 | push_parser_t *parser, 158 | size_t size) 159 | { 160 | void *context; 161 | push_callback_t *fixed; 162 | push_callback_t *min_bytes; 163 | 164 | /* 165 | * Create a memory context for the objects we're about to create. 166 | */ 167 | 168 | context = push_talloc_new(parent); 169 | if (context == NULL) return NULL; 170 | 171 | /* 172 | * Create the callbacks. 173 | */ 174 | 175 | if (name == NULL) name = "fixed"; 176 | 177 | fixed = inner_fixed_new 178 | (push_talloc_asprintf(context, "%s.inner", name), 179 | context, parser, size); 180 | min_bytes = push_min_bytes_new 181 | (push_talloc_asprintf(context, "%s.min-bytes", name), 182 | context, parser, fixed, size); 183 | 184 | /* 185 | * Because of NULL propagation, we only have to check the last 186 | * result to see if everything was created okay. 187 | */ 188 | 189 | if (min_bytes == NULL) goto error; 190 | 191 | return min_bytes; 192 | 193 | error: 194 | /* 195 | * Before returning, free any objects we created before the error. 196 | */ 197 | 198 | push_talloc_free(context); 199 | return NULL; 200 | } 201 | -------------------------------------------------------------------------------- /src/skip.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | /** 19 | * The user data struct for a skip callback. 20 | */ 21 | 22 | typedef struct _skip 23 | { 24 | /** 25 | * The push_callback_t superclass for this callback. 26 | */ 27 | 28 | push_callback_t callback; 29 | 30 | /** 31 | * The continue continuation for this callback. 32 | */ 33 | 34 | push_continue_continuation_t cont; 35 | 36 | /** 37 | * The total number of bytes to skip. 38 | */ 39 | 40 | size_t total_to_skip; 41 | 42 | /** 43 | * The number of bytes left to skip. 44 | */ 45 | 46 | size_t left_to_skip; 47 | 48 | } skip_t; 49 | 50 | 51 | static void 52 | skip_continue(void *user_data, 53 | const void *buf, 54 | size_t bytes_remaining) 55 | { 56 | skip_t *skip = (skip_t *) user_data; 57 | size_t bytes_to_skip; 58 | 59 | /* 60 | * If we reach EOF, then it's a parse error, since we didn't 61 | * receive as many bytes as we needed to skip over. 62 | */ 63 | 64 | if (bytes_remaining == 0) 65 | { 66 | PUSH_DEBUG_MSG("%s: Reached EOF still needing to skip " 67 | "%zu bytes.\n", 68 | push_talloc_get_name(skip), 69 | skip->left_to_skip); 70 | 71 | push_continuation_call(skip->callback.error, 72 | PUSH_PARSE_ERROR, 73 | "Reached EOF before end of skip"); 74 | 75 | return; 76 | } 77 | 78 | /* 79 | * Skip over the data in the current chunk. 80 | */ 81 | 82 | bytes_to_skip = 83 | (bytes_remaining > skip->left_to_skip)? 84 | skip->left_to_skip: 85 | bytes_remaining; 86 | 87 | PUSH_DEBUG_MSG("%s: Skipping over %zu bytes.\n", 88 | push_talloc_get_name(skip), 89 | bytes_to_skip); 90 | 91 | buf += bytes_to_skip; 92 | bytes_remaining -= bytes_to_skip; 93 | skip->left_to_skip -= bytes_to_skip; 94 | 95 | /* 96 | * If we've skipped over everything we need to, then fire off a 97 | * success result. 98 | */ 99 | 100 | if (skip->left_to_skip == 0) 101 | { 102 | PUSH_DEBUG_MSG("%s: Finished skipping.\n", 103 | push_talloc_get_name(skip)); 104 | 105 | push_continuation_call(skip->callback.success, 106 | NULL, 107 | buf, bytes_remaining); 108 | 109 | return; 110 | } 111 | 112 | /* 113 | * Otherwise, we return an incomplete result. 114 | */ 115 | 116 | PUSH_DEBUG_MSG("%s: %zu bytes left to skip.\n", 117 | push_talloc_get_name(skip), 118 | skip->left_to_skip); 119 | 120 | push_continuation_call(skip->callback.incomplete, 121 | &skip->cont); 122 | } 123 | 124 | 125 | static void 126 | skip_activate(void *user_data, 127 | void *result, 128 | const void *buf, 129 | size_t bytes_remaining) 130 | { 131 | skip_t *skip = (skip_t *) user_data; 132 | size_t *bytes_to_skip = (size_t *) result; 133 | 134 | PUSH_DEBUG_MSG("%s: Activating. Will skip %zu bytes.\n", 135 | push_talloc_get_name(skip), 136 | *bytes_to_skip); 137 | 138 | skip->total_to_skip = *bytes_to_skip; 139 | skip->left_to_skip = *bytes_to_skip; 140 | 141 | if (bytes_remaining == 0) 142 | { 143 | /* 144 | * If we don't get any data when we're activated, return an 145 | * incomplete and wait for some data. 146 | */ 147 | 148 | push_continuation_call(skip->callback.incomplete, 149 | &skip->cont); 150 | 151 | return; 152 | 153 | } else { 154 | /* 155 | * Otherwise let the continue continuation go ahead and 156 | * process this chunk of data. 157 | */ 158 | 159 | skip_continue(user_data, buf, bytes_remaining); 160 | return; 161 | } 162 | } 163 | 164 | 165 | push_callback_t * 166 | push_skip_new(const char *name, 167 | void *parent, 168 | push_parser_t *parser) 169 | { 170 | skip_t *skip = push_talloc(parent, skip_t); 171 | 172 | if (skip == NULL) 173 | return NULL; 174 | 175 | /* 176 | * Initialize the push_callback_t instance. 177 | */ 178 | 179 | if (name == NULL) name = "skip"; 180 | push_talloc_set_name_const(skip, name); 181 | 182 | push_callback_init(&skip->callback, parser, skip, 183 | skip_activate, 184 | NULL, NULL, NULL); 185 | 186 | /* 187 | * Fill in the continuation objects for the continuations that we 188 | * implement. 189 | */ 190 | 191 | push_continuation_set(&skip->cont, 192 | skip_continue, 193 | skip); 194 | 195 | return &skip->callback; 196 | } 197 | -------------------------------------------------------------------------------- /src/pairs/first.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | /** 17 | * The push_callback_t subclass that defines a first callback. 18 | */ 19 | 20 | typedef struct _first 21 | { 22 | /** 23 | * The push_callback_t superclass for this callback. 24 | */ 25 | 26 | push_callback_t callback; 27 | 28 | /** 29 | * The success continuation that we have the wrapped callback use. 30 | * It constructs the output pair from the wrapped callback's 31 | * result. 32 | */ 33 | 34 | push_success_continuation_t wrapped_success; 35 | 36 | /** 37 | * The wrapped callback. 38 | */ 39 | 40 | push_callback_t *wrapped; 41 | 42 | /** 43 | * The value that we copy through the second element of the pair. 44 | */ 45 | 46 | void *second; 47 | 48 | /** 49 | * The pair object that we output as our result. 50 | */ 51 | 52 | push_pair_t result; 53 | 54 | } first_t; 55 | 56 | 57 | static void 58 | first_set_incomplete(void *user_data, 59 | push_incomplete_continuation_t *incomplete) 60 | { 61 | first_t *first = (first_t *) user_data; 62 | push_continuation_call(&first->wrapped->set_incomplete, 63 | incomplete); 64 | } 65 | 66 | 67 | static void 68 | first_set_error(void *user_data, 69 | push_error_continuation_t *error) 70 | { 71 | first_t *first = (first_t *) user_data; 72 | push_continuation_call(&first->wrapped->set_error, 73 | error); 74 | } 75 | 76 | 77 | static void 78 | first_activate(void *user_data, 79 | void *result, 80 | const void *buf, 81 | size_t bytes_remaining) 82 | { 83 | first_t *first = (first_t *) user_data; 84 | push_pair_t *input = (push_pair_t *) result; 85 | 86 | /* 87 | * Save the second element of the input pair, so that we can copy 88 | * it into our result later. 89 | */ 90 | 91 | first->second = input->second; 92 | 93 | /* 94 | * We activate this callback by passing the first element of the 95 | * input pair into our wrapped callback. 96 | */ 97 | 98 | PUSH_DEBUG_MSG("%s: Activating wrapped callback.\n", 99 | push_talloc_get_name(first)); 100 | 101 | push_continuation_call(&first->wrapped->activate, 102 | input->first, 103 | buf, bytes_remaining); 104 | 105 | return; 106 | } 107 | 108 | 109 | static void 110 | first_wrapped_success(void *user_data, 111 | void *result, 112 | const void *buf, 113 | size_t bytes_remaining) 114 | { 115 | first_t *first = (first_t *) user_data; 116 | 117 | /* 118 | * Create the output pair from this result and our saved value. 119 | */ 120 | 121 | PUSH_DEBUG_MSG("%s: Constructing output pair.\n", 122 | push_talloc_get_name(first)); 123 | 124 | first->result.first = result; 125 | first->result.second = first->second; 126 | 127 | push_continuation_call(first->callback.success, 128 | &first->result, 129 | buf, bytes_remaining); 130 | 131 | return; 132 | } 133 | 134 | 135 | push_callback_t * 136 | push_first_new(const char *name, 137 | void *parent, 138 | push_parser_t *parser, 139 | push_callback_t *wrapped) 140 | { 141 | first_t *first; 142 | 143 | /* 144 | * If the wrapped callback is NULL, return NULL ourselves. 145 | */ 146 | 147 | if (wrapped == NULL) 148 | return NULL; 149 | 150 | /* 151 | * Allocate the user data struct. 152 | */ 153 | 154 | first = push_talloc(parent, first_t); 155 | if (first == NULL) return NULL; 156 | 157 | /* 158 | * Make the wrapped callback a child of the new callback. 159 | */ 160 | 161 | push_talloc_steal(first, wrapped); 162 | 163 | /* 164 | * Fill in the data items. 165 | */ 166 | 167 | first->wrapped = wrapped; 168 | 169 | /* 170 | * Initialize the push_callback_t instance. 171 | */ 172 | 173 | if (name == NULL) name = "first"; 174 | push_talloc_set_name_const(first, name); 175 | 176 | push_callback_init(&first->callback, parser, first, 177 | first_activate, 178 | NULL, 179 | first_set_incomplete, 180 | first_set_error); 181 | 182 | /* 183 | * Fill in the continuation objects for the continuations that we 184 | * implement. 185 | */ 186 | 187 | push_continuation_set(&first->wrapped_success, 188 | first_wrapped_success, 189 | first); 190 | 191 | /* 192 | * The wrapped callback should succeed by calling our 193 | * wrapped_succeed continuation, so that we can construct the 194 | * output pair. 195 | */ 196 | 197 | push_continuation_call(&wrapped->set_success, 198 | &first->wrapped_success); 199 | 200 | return &first->callback; 201 | } 202 | -------------------------------------------------------------------------------- /tests/indexed-sum-callback.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | /*----------------------------------------------------------------------- 24 | * Indexed sum callback implementation 25 | * 26 | * The indexed sum callback is implemented like the sum callback, 27 | * using pairs. The first element of the pair is itself a pair — two 28 | * parsed integer, the index and the value. The second element of the 29 | * pair is an array of sums. 30 | */ 31 | 32 | 33 | typedef struct _inner_sum 34 | { 35 | uint32_t num_sums; 36 | } inner_sum_t; 37 | 38 | 39 | static bool 40 | inner_sum_func(uint32_t *num_sums, 41 | push_pair_t *input, 42 | uint32_t **output) 43 | { 44 | push_pair_t *input_ints = (push_pair_t *) input->first; 45 | uint32_t *input_index = (uint32_t *) input_ints->first; 46 | uint32_t *input_int = (uint32_t *) input_ints->second; 47 | uint32_t *input_sums = (uint32_t *) input->second; 48 | 49 | PUSH_DEBUG_MSG("inner-sum: Activating callback. " 50 | "Received value %"PRIu32" for index " 51 | "%"PRIu32".\n", 52 | *input_int, 53 | *input_index); 54 | 55 | if ((*input_index < 0) || (*input_index >= *num_sums)) 56 | { 57 | PUSH_DEBUG_MSG("inner-sum: Index is out of range.\n"); 58 | return false; 59 | } 60 | 61 | PUSH_DEBUG_MSG("inner-sum: Previous sum #%"PRIu32" was " 62 | "%"PRIu32".\n", 63 | *input_index, 64 | input_sums[*input_index]); 65 | 66 | input_sums[*input_index] += *input_int; 67 | 68 | PUSH_DEBUG_MSG("inner-sum: Adding, sum is now %"PRIu32"\n", 69 | input_sums[*input_index]); 70 | 71 | *output = input_sums; 72 | return true; 73 | } 74 | 75 | 76 | push_define_pure_data_callback(inner_sum_new, inner_sum_func, 77 | "inner-sum", 78 | push_pair_t, uint32_t, uint32_t); 79 | 80 | 81 | static push_callback_t * 82 | inner_sum_callback_new(const char *name, 83 | void *parent, 84 | push_parser_t *parser, 85 | uint32_t num_sums) 86 | { 87 | push_callback_t *inner_sum; 88 | uint32_t *user_num_sums; 89 | 90 | inner_sum = inner_sum_new(name, parent, parser, 91 | &user_num_sums); 92 | if (inner_sum == NULL) return NULL; 93 | 94 | *user_num_sums = num_sums; 95 | return inner_sum; 96 | } 97 | 98 | 99 | push_callback_t * 100 | indexed_sum_callback_new(const char *name, 101 | void *parent, 102 | push_parser_t *parser, 103 | uint32_t num_sums) 104 | { 105 | void *context; 106 | push_callback_t *dup; 107 | push_callback_t *integer; 108 | push_callback_t *index; 109 | push_callback_t *both; 110 | push_callback_t *first; 111 | push_callback_t *inner_sum; 112 | push_callback_t *compose1; 113 | push_callback_t *compose2; 114 | 115 | /* 116 | * Create a memory context for the objects we're about to create. 117 | */ 118 | 119 | context = push_talloc_new(parent); 120 | if (context == NULL) return NULL; 121 | 122 | /* 123 | * Then create the callbacks. 124 | */ 125 | 126 | if (name == NULL) name = "indexed-sum"; 127 | 128 | dup = push_dup_new 129 | (push_talloc_asprintf(context, "%s.dup", name), 130 | context, parser); 131 | index = integer_callback_new 132 | (push_talloc_asprintf(context, "%s.index", name), 133 | context, parser); 134 | integer = integer_callback_new 135 | (push_talloc_asprintf(context, "%s.integer", name), 136 | context, parser); 137 | both = push_both_new 138 | (push_talloc_asprintf(context, "%s.both", name), 139 | context, parser, index, integer); 140 | first = push_first_new 141 | (push_talloc_asprintf(context, "%s.first", name), 142 | context, parser, both); 143 | inner_sum = inner_sum_callback_new 144 | (push_talloc_asprintf(context, "%s.inner", name), 145 | context, 146 | parser, num_sums); 147 | compose1 = push_compose_new 148 | (push_talloc_asprintf(context, "%s.compose1", name), 149 | context, 150 | parser, dup, first); 151 | compose2 = push_compose_new 152 | (push_talloc_asprintf(context, "%s.compose2", name), 153 | context, 154 | parser, compose1, inner_sum); 155 | 156 | /* 157 | * Because of NULL propagation, we only have to check the last 158 | * result to see if everything was created okay. 159 | */ 160 | 161 | if (compose2 == NULL) goto error; 162 | return compose2; 163 | 164 | error: 165 | /* 166 | * Before returning, free any objects we created before the error. 167 | */ 168 | 169 | push_talloc_free(context); 170 | return NULL; 171 | } 172 | -------------------------------------------------------------------------------- /src/pairs/second.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | /** 17 | * The push_callback_t subclass that defines a second callback. 18 | */ 19 | 20 | typedef struct _second 21 | { 22 | /** 23 | * The push_callback_t superclass for this callback. 24 | */ 25 | 26 | push_callback_t callback; 27 | 28 | /** 29 | * The success continuation that we have the wrapped callback use. 30 | * It constructs the output pair from the wrapped callback's 31 | * result. 32 | */ 33 | 34 | push_success_continuation_t wrapped_success; 35 | 36 | /** 37 | * The wrapped callback. 38 | */ 39 | 40 | push_callback_t *wrapped; 41 | 42 | /** 43 | * The value that we copy through the first element of the pair. 44 | */ 45 | 46 | void *first; 47 | 48 | /** 49 | * The pair object that we output as our result. 50 | */ 51 | 52 | push_pair_t result; 53 | 54 | } second_t; 55 | 56 | 57 | static void 58 | second_set_incomplete(void *user_data, 59 | push_incomplete_continuation_t *incomplete) 60 | { 61 | second_t *second = (second_t *) user_data; 62 | push_continuation_call(&second->wrapped->set_incomplete, 63 | incomplete); 64 | } 65 | 66 | 67 | static void 68 | second_set_error(void *user_data, 69 | push_error_continuation_t *error) 70 | { 71 | second_t *second = (second_t *) user_data; 72 | push_continuation_call(&second->wrapped->set_error, 73 | error); 74 | } 75 | 76 | 77 | static void 78 | second_activate(void *user_data, 79 | void *result, 80 | const void *buf, 81 | size_t bytes_remaining) 82 | { 83 | second_t *second = (second_t *) user_data; 84 | push_pair_t *input = (push_pair_t *) result; 85 | 86 | /* 87 | * Save the first element of the input pair, so that we can copy 88 | * it into our result later. 89 | */ 90 | 91 | second->first = input->first; 92 | 93 | /* 94 | * We activate this callback by passing the second element of the 95 | * input pair into our wrapped callback. 96 | */ 97 | 98 | PUSH_DEBUG_MSG("%s: Activating wrapped callback.\n", 99 | push_talloc_get_name(second)); 100 | 101 | push_continuation_call(&second->wrapped->activate, 102 | input->second, 103 | buf, bytes_remaining); 104 | 105 | return; 106 | } 107 | 108 | 109 | static void 110 | second_wrapped_success(void *user_data, 111 | void *result, 112 | const void *buf, 113 | size_t bytes_remaining) 114 | { 115 | second_t *second = (second_t *) user_data; 116 | 117 | /* 118 | * Create the output pair from this result and our saved value. 119 | */ 120 | 121 | PUSH_DEBUG_MSG("%s: Constructing output pair.\n", 122 | push_talloc_get_name(second)); 123 | 124 | second->result.first = second->first; 125 | second->result.second = result; 126 | 127 | push_continuation_call(second->callback.success, 128 | &second->result, 129 | buf, bytes_remaining); 130 | 131 | return; 132 | } 133 | 134 | 135 | push_callback_t * 136 | push_second_new(const char *name, 137 | void *parent, 138 | push_parser_t *parser, 139 | push_callback_t *wrapped) 140 | { 141 | second_t *second; 142 | 143 | /* 144 | * If the wrapped callback is NULL, return NULL ourselves. 145 | */ 146 | 147 | if (wrapped == NULL) 148 | return NULL; 149 | 150 | /* 151 | * Allocate the user data struct. 152 | */ 153 | 154 | second = push_talloc(parent, second_t); 155 | if (second == NULL) return NULL; 156 | 157 | /* 158 | * Make the wrapped callback a child of the new callback. 159 | */ 160 | 161 | push_talloc_steal(second, wrapped); 162 | 163 | /* 164 | * Fill in the data items. 165 | */ 166 | 167 | second->wrapped = wrapped; 168 | 169 | /* 170 | * Initialize the push_callback_t instance. 171 | */ 172 | 173 | if (name == NULL) name = "second"; 174 | push_talloc_set_name_const(second, name); 175 | 176 | push_callback_init(&second->callback, parser, second, 177 | second_activate, 178 | NULL, 179 | second_set_incomplete, 180 | second_set_error); 181 | 182 | /* 183 | * Fill in the continuation objects for the continuations that we 184 | * implement. 185 | */ 186 | 187 | push_continuation_set(&second->wrapped_success, 188 | second_wrapped_success, 189 | second); 190 | 191 | /* 192 | * The wrapped callback should succeed by calling our 193 | * wrapped_succeed continuation, so that we can construct the 194 | * output pair. 195 | */ 196 | 197 | push_continuation_call(&wrapped->set_success, 198 | &second->wrapped_success); 199 | 200 | return &second->callback; 201 | } 202 | -------------------------------------------------------------------------------- /tests/test-protobuf-skip-length-prefixed.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | 23 | /*----------------------------------------------------------------------- 24 | * Sample data 25 | */ 26 | 27 | const uint8_t DATA_01[] = 28 | "\x05" /* supposed length: 5 */ 29 | "12345"; /* actual length: 5 */ 30 | const size_t LENGTH_01 = 6; 31 | 32 | 33 | const uint8_t DATA_02[] = 34 | "\x05" /* supposed length: 5 */ 35 | "1234567"; /* actual length: 7 */ 36 | const size_t LENGTH_02 = 8; 37 | 38 | 39 | const uint8_t DATA_03[] = 40 | "\x05" /* supposed length: 5 */ 41 | "123"; /* actual length: 3 */ 42 | const size_t LENGTH_03 = 4; 43 | 44 | 45 | /*----------------------------------------------------------------------- 46 | * Test cases 47 | */ 48 | 49 | 50 | START_TEST(test_skip_01) 51 | { 52 | push_parser_t *parser; 53 | push_callback_t *callback; 54 | 55 | PUSH_DEBUG_MSG("---\nStarting test_skip_01\n"); 56 | 57 | /* 58 | * Skip over five bytes, and provide 5 bytes. This should 59 | * succeed. 60 | */ 61 | 62 | parser = push_parser_new(); 63 | fail_if(parser == NULL, 64 | "Could not allocate a new push parser"); 65 | 66 | callback = push_protobuf_skip_length_prefixed_new 67 | ("skip", NULL, parser); 68 | fail_if(callback == NULL, 69 | "Could not allocate a new skip callback"); 70 | 71 | push_parser_set_callback(parser, callback); 72 | 73 | fail_unless(push_parser_activate(parser, NULL) 74 | == PUSH_INCOMPLETE, 75 | "Could not activate parser"); 76 | 77 | fail_unless(push_parser_submit_data 78 | (parser, &DATA_01, LENGTH_01) == PUSH_SUCCESS, 79 | "Could not parse data"); 80 | 81 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 82 | "Shouldn't get parse error at EOF"); 83 | 84 | push_parser_free(parser); 85 | } 86 | END_TEST 87 | 88 | 89 | START_TEST(test_skip_02) 90 | { 91 | push_parser_t *parser; 92 | push_callback_t *callback; 93 | 94 | PUSH_DEBUG_MSG("---\nStarting test_skip_02\n"); 95 | 96 | /* 97 | * Skip over five bytes, and provide 7 bytes. This should 98 | * succeed. 99 | */ 100 | 101 | parser = push_parser_new(); 102 | fail_if(parser == NULL, 103 | "Could not allocate a new push parser"); 104 | 105 | callback = push_protobuf_skip_length_prefixed_new 106 | ("skip", NULL, parser); 107 | fail_if(callback == NULL, 108 | "Could not allocate a new skip callback"); 109 | 110 | push_parser_set_callback(parser, callback); 111 | 112 | fail_unless(push_parser_activate(parser, NULL) 113 | == PUSH_INCOMPLETE, 114 | "Could not activate parser"); 115 | 116 | fail_unless(push_parser_submit_data 117 | (parser, &DATA_02, LENGTH_02) == PUSH_SUCCESS, 118 | "Could not parse data"); 119 | 120 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 121 | "Shouldn't get parse error at EOF"); 122 | 123 | push_parser_free(parser); 124 | } 125 | END_TEST 126 | 127 | 128 | START_TEST(test_skip_03) 129 | { 130 | push_parser_t *parser; 131 | push_callback_t *callback; 132 | 133 | PUSH_DEBUG_MSG("---\nStarting test_skip_03\n"); 134 | 135 | /* 136 | * Skip over five bytes, and provide 3 bytes. This should fail. 137 | */ 138 | 139 | parser = push_parser_new(); 140 | fail_if(parser == NULL, 141 | "Could not allocate a new push parser"); 142 | 143 | callback = push_protobuf_skip_length_prefixed_new 144 | ("skip", NULL, parser); 145 | fail_if(callback == NULL, 146 | "Could not allocate a new skip callback"); 147 | 148 | push_parser_set_callback(parser, callback); 149 | 150 | fail_unless(push_parser_activate(parser, NULL) 151 | == PUSH_INCOMPLETE, 152 | "Could not activate parser"); 153 | 154 | fail_unless(push_parser_submit_data 155 | (parser, &DATA_03, LENGTH_03) == PUSH_INCOMPLETE, 156 | "Could not parse data"); 157 | 158 | fail_unless(push_parser_eof(parser) == PUSH_PARSE_ERROR, 159 | "Should get parse error at EOF"); 160 | 161 | push_parser_free(parser); 162 | } 163 | END_TEST 164 | 165 | 166 | /*----------------------------------------------------------------------- 167 | * Testing harness 168 | */ 169 | 170 | Suite * 171 | test_suite() 172 | { 173 | Suite *s = suite_create("skip"); 174 | 175 | TCase *tc = tcase_create("skip"); 176 | tcase_add_test(tc, test_skip_01); 177 | tcase_add_test(tc, test_skip_02); 178 | tcase_add_test(tc, test_skip_03); 179 | suite_add_tcase(s, tc); 180 | 181 | return s; 182 | } 183 | 184 | 185 | int 186 | main(int argc, const char **argv) 187 | { 188 | int number_failed; 189 | Suite *suite = test_suite(); 190 | SRunner *runner = srunner_create(suite); 191 | 192 | srunner_run_all(runner, CK_NORMAL); 193 | number_failed = srunner_ntests_failed(runner); 194 | srunner_free(runner); 195 | 196 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 197 | } 198 | -------------------------------------------------------------------------------- /tests/test-hwm.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | 24 | /*----------------------------------------------------------------------- 25 | * Sample data 26 | */ 27 | 28 | const uint8_t DATA_01[] = "1234567890"; 29 | const size_t LENGTH_01 = 10; 30 | 31 | 32 | /*----------------------------------------------------------------------- 33 | * Test cases 34 | */ 35 | 36 | 37 | START_TEST(test_hwm_string_01) 38 | { 39 | push_parser_t *parser; 40 | push_callback_t *callback; 41 | hwm_buffer_t buf; 42 | void *result; 43 | size_t bytes_to_read = 5; 44 | 45 | PUSH_DEBUG_MSG("---\nStarting test_hwm_string_01\n"); 46 | 47 | /* 48 | * Read five bytes, and provide 5 bytes. This should succeed. 49 | */ 50 | 51 | hwm_buffer_init(&buf); 52 | 53 | parser = push_parser_new(); 54 | fail_if(parser == NULL, 55 | "Could not allocate a new push parser"); 56 | 57 | callback = push_hwm_string_new("hwm", NULL, parser, &buf); 58 | fail_if(callback == NULL, 59 | "Could not allocate a new HWM-string callback"); 60 | 61 | push_parser_set_callback(parser, callback); 62 | 63 | fail_unless(push_parser_activate(parser, &bytes_to_read) 64 | == PUSH_INCOMPLETE, 65 | "Could not activate parser"); 66 | 67 | fail_unless(push_parser_submit_data 68 | (parser, &DATA_01, 5) == PUSH_SUCCESS, 69 | "Could not parse data"); 70 | 71 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 72 | "Shouldn't get parse error at EOF"); 73 | 74 | result = push_parser_result(parser, void); 75 | fail_unless(memcmp(result, &DATA_01, bytes_to_read) == 0, 76 | "Data doesn't match"); 77 | 78 | push_parser_free(parser); 79 | hwm_buffer_done(&buf); 80 | } 81 | END_TEST 82 | 83 | 84 | START_TEST(test_hwm_string_02) 85 | { 86 | push_parser_t *parser; 87 | push_callback_t *callback; 88 | hwm_buffer_t buf; 89 | void *result; 90 | size_t bytes_to_read = 5; 91 | 92 | PUSH_DEBUG_MSG("---\nStarting test_hwm_string_02\n"); 93 | 94 | /* 95 | * Read five bytes, and provide 7 bytes. This should succeed. 96 | */ 97 | 98 | hwm_buffer_init(&buf); 99 | 100 | parser = push_parser_new(); 101 | fail_if(parser == NULL, 102 | "Could not allocate a new push parser"); 103 | 104 | callback = push_hwm_string_new("hwm", NULL, parser, &buf); 105 | fail_if(callback == NULL, 106 | "Could not allocate a new HWM-string callback"); 107 | 108 | push_parser_set_callback(parser, callback); 109 | 110 | fail_unless(push_parser_activate(parser, &bytes_to_read) 111 | == PUSH_INCOMPLETE, 112 | "Could not activate parser"); 113 | 114 | fail_unless(push_parser_submit_data 115 | (parser, &DATA_01, 7) == PUSH_SUCCESS, 116 | "Could not parse data"); 117 | 118 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 119 | "Shouldn't get parse error at EOF"); 120 | 121 | result = push_parser_result(parser, void); 122 | fail_unless(memcmp(result, &DATA_01, bytes_to_read) == 0, 123 | "Data doesn't match"); 124 | 125 | push_parser_free(parser); 126 | hwm_buffer_done(&buf); 127 | } 128 | END_TEST 129 | 130 | 131 | START_TEST(test_hwm_string_03) 132 | { 133 | push_parser_t *parser; 134 | push_callback_t *callback; 135 | hwm_buffer_t buf; 136 | size_t bytes_to_read = 5; 137 | 138 | /* 139 | * Read five bytes, and provide 3 bytes. This should fail. 140 | */ 141 | 142 | PUSH_DEBUG_MSG("---\nStarting test_hwm_string_03\n"); 143 | 144 | hwm_buffer_init(&buf); 145 | 146 | parser = push_parser_new(); 147 | fail_if(parser == NULL, 148 | "Could not allocate a new push parser"); 149 | 150 | callback = push_hwm_string_new("hwm", NULL, parser, &buf); 151 | fail_if(callback == NULL, 152 | "Could not allocate a new HWM-string callback"); 153 | 154 | push_parser_set_callback(parser, callback); 155 | 156 | fail_unless(push_parser_activate(parser, &bytes_to_read) 157 | == PUSH_INCOMPLETE, 158 | "Could not activate parser"); 159 | 160 | fail_unless(push_parser_submit_data 161 | (parser, &DATA_01, 3) == PUSH_INCOMPLETE, 162 | "Could not parse data"); 163 | 164 | fail_unless(push_parser_eof(parser) == PUSH_PARSE_ERROR, 165 | "Should get parse error at EOF"); 166 | 167 | push_parser_free(parser); 168 | hwm_buffer_done(&buf); 169 | } 170 | END_TEST 171 | 172 | 173 | /*----------------------------------------------------------------------- 174 | * Testing harness 175 | */ 176 | 177 | Suite * 178 | test_suite() 179 | { 180 | Suite *s = suite_create("hwm_string"); 181 | 182 | TCase *tc = tcase_create("hwm_string"); 183 | tcase_add_test(tc, test_hwm_string_01); 184 | tcase_add_test(tc, test_hwm_string_02); 185 | tcase_add_test(tc, test_hwm_string_03); 186 | suite_add_tcase(s, tc); 187 | 188 | return s; 189 | } 190 | 191 | 192 | int 193 | main(int argc, const char **argv) 194 | { 195 | int number_failed; 196 | Suite *suite = test_suite(); 197 | SRunner *runner = srunner_create(suite); 198 | 199 | srunner_run_all(runner, CK_NORMAL); 200 | number_failed = srunner_ntests_failed(runner); 201 | srunner_free(runner); 202 | 203 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 204 | } 205 | -------------------------------------------------------------------------------- /src/parser.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | 19 | static void 20 | parser_success(void *user_data, 21 | void *result, 22 | const void *buf, 23 | size_t bytes_remaining) 24 | { 25 | push_parser_t *parser = (push_parser_t *) user_data; 26 | 27 | PUSH_DEBUG_MSG("parser: Parse successful with final result %p.\n", 28 | result); 29 | 30 | PUSH_DEBUG_MSG("parser: %zu bytes remaining in current chunk.\n", 31 | bytes_remaining); 32 | 33 | /* 34 | * When the last callback finishes with a successful parse, save 35 | * the result code and the callback's output value. 36 | */ 37 | 38 | parser->result_code = PUSH_SUCCESS; 39 | parser->result = result; 40 | 41 | /* 42 | * Register a continue callback that will ignore any further data. 43 | */ 44 | 45 | parser->cont = &parser->ignore; 46 | } 47 | 48 | 49 | static void 50 | parser_incomplete(void *user_data, 51 | push_continue_continuation_t *cont) 52 | { 53 | push_parser_t *parser = (push_parser_t *) user_data; 54 | 55 | PUSH_DEBUG_MSG("parser: Finished parsing this chunk, " 56 | "parse incomplete.\n"); 57 | 58 | /* 59 | * When the current callback returns an incomplete, register its 60 | * continue continuation, so that we can send the next chunk of 61 | * data to it. 62 | */ 63 | 64 | parser->cont = cont; 65 | 66 | /* 67 | * Save the incomplete result code. 68 | */ 69 | 70 | parser->result_code = PUSH_INCOMPLETE; 71 | } 72 | 73 | 74 | static void 75 | parser_error(void *user_data, 76 | push_error_code_t error_code, 77 | const char *error_message) 78 | { 79 | push_parser_t *parser = (push_parser_t *) user_data; 80 | 81 | PUSH_DEBUG_MSG("parser: Parse fails with error code %d.\n", 82 | error_code); 83 | 84 | /* 85 | * When the last callback finishes with an error, save the result 86 | * code. 87 | */ 88 | 89 | parser->result_code = error_code; 90 | } 91 | 92 | 93 | static void 94 | parser_ignore(void *user_data, 95 | const void *buf, 96 | size_t bytes_remaining) 97 | { 98 | /* 99 | * Don't do anything. 100 | */ 101 | 102 | PUSH_DEBUG_MSG("parser: Skipping %zu bytes after finished parse.\n", 103 | bytes_remaining); 104 | } 105 | 106 | 107 | push_parser_t * 108 | push_parser_new(push_callback_t *callback) 109 | { 110 | push_parser_t *result; 111 | 112 | /* 113 | * First try to allocate the new parser instance. If we can't, 114 | * return NULL. 115 | */ 116 | 117 | result = push_talloc(NULL, push_parser_t); 118 | if (result == NULL) 119 | { 120 | return NULL; 121 | } 122 | 123 | /* 124 | * Initialize the continuations that we implement. 125 | */ 126 | 127 | push_continuation_set(&result->success, 128 | parser_success, 129 | result); 130 | 131 | push_continuation_set(&result->incomplete, 132 | parser_incomplete, 133 | result); 134 | 135 | push_continuation_set(&result->error, 136 | parser_error, 137 | result); 138 | 139 | push_continuation_set(&result->ignore, 140 | parser_ignore, 141 | result); 142 | 143 | return result; 144 | } 145 | 146 | 147 | void 148 | push_parser_free(push_parser_t *parser) 149 | { 150 | /* 151 | * We create all of the callback objects using push_talloc, using 152 | * the parser object as its parent context. That means that 153 | * calling push_talloc_free on the parser will free all of the 154 | * callbacks, too. 155 | */ 156 | 157 | push_talloc_free(parser); 158 | } 159 | 160 | 161 | void 162 | push_parser_set_callback(push_parser_t *parser, 163 | push_callback_t *callback) 164 | { 165 | /* 166 | * The parser should call the callback's activation callback when 167 | * it starts. 168 | */ 169 | 170 | parser->activate = &callback->activate; 171 | 172 | /* 173 | * There is no initial continue continuation; this will be set 174 | * when a parser returns an incomplete. 175 | */ 176 | 177 | parser->cont = NULL; 178 | 179 | /* 180 | * Take control of the callback, so that it gets freed when the 181 | * parser does. 182 | */ 183 | 184 | push_talloc_steal(parser, callback); 185 | } 186 | 187 | 188 | push_error_code_t 189 | push_parser_activate(push_parser_t *parser, 190 | void *input) 191 | { 192 | PUSH_DEBUG_MSG("parser: Activating with input pointer %p.\n", 193 | input); 194 | 195 | /* 196 | * We activate the initial callback without any data. In most 197 | * cases, this will cause it to return incomplete. 198 | */ 199 | 200 | push_continuation_call(parser->activate, input, NULL, 0); 201 | 202 | /* 203 | * Eventually, the callback will call one of the parser's 204 | * continuations, which will set the result_code field. 205 | */ 206 | 207 | return parser->result_code; 208 | } 209 | 210 | 211 | push_error_code_t 212 | push_parser_submit_data(push_parser_t *parser, 213 | const void *buf, 214 | size_t bytes_available) 215 | { 216 | PUSH_DEBUG_MSG("parser: Processing %zu bytes at %p.\n", 217 | bytes_available, buf); 218 | 219 | /* 220 | * Pass the data into the current continue continuation. 221 | */ 222 | 223 | push_continuation_call(parser->cont, buf, bytes_available); 224 | 225 | /* 226 | * Eventually, the callback will call one of the parser's 227 | * continuations, which will set the result_code field. 228 | */ 229 | 230 | return parser->result_code; 231 | } 232 | 233 | 234 | push_error_code_t 235 | push_parser_eof(push_parser_t *parser) 236 | { 237 | PUSH_DEBUG_MSG("parser: EOF received.\n"); 238 | 239 | /* 240 | * Pass the EOF into the current continue continuation. 241 | */ 242 | 243 | push_continuation_call(parser->cont, NULL, 0); 244 | 245 | /* 246 | * Eventually, the callback will call one of the parser's 247 | * continuations, which will set the result_code field. 248 | */ 249 | 250 | return parser->result_code; 251 | } 252 | -------------------------------------------------------------------------------- /include/push/typesafe_cb.h: -------------------------------------------------------------------------------- 1 | #ifndef CCAN_CAST_IF_TYPE_H 2 | #define CCAN_CAST_IF_TYPE_H 3 | #include "config.h" 4 | 5 | #if HAVE_TYPEOF && HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_TYPES_COMPATIBLE_P 6 | /** 7 | * cast_if_type - only cast an expression if it is of a given type 8 | * @expr: the expression to cast 9 | * @oktype: the type we allow 10 | * @desttype: the type to cast to 11 | * 12 | * This macro is used to create functions which allow multiple types. 13 | * The result of this macro is used somewhere that a @desttype type is 14 | * expected: if @expr was of type @oktype, it will be cast to 15 | * @desttype type. As a result, if @expr is any type other than 16 | * @oktype or @desttype, a compiler warning will be issued. 17 | * 18 | * This macro can be used in static initializers. 19 | * 20 | * This is merely useful for warnings: if the compiler does not 21 | * support the primitives required for cast_if_type(), it becomes an 22 | * unconditional cast, and the @oktype argument is not used. In 23 | * particular, this means that @oktype can be a type which uses 24 | * the "typeof": it will not be evaluated if typeof is not supported. 25 | * 26 | * Example: 27 | * // We can take either an unsigned long or a void *. 28 | * void _set_some_value(void *val); 29 | * #define set_some_value(expr) \ 30 | * _set_some_value(cast_if_type((expr), unsigned long, void *)) 31 | */ 32 | #define cast_if_type(expr, oktype, desttype) \ 33 | __builtin_choose_expr(__builtin_types_compatible_p(typeof(1?(expr):0), oktype), \ 34 | (desttype)(expr), (expr)) 35 | #else 36 | #define cast_if_type(expr, oktype, desttype) ((desttype)(expr)) 37 | #endif 38 | 39 | /** 40 | * typesafe_cb - cast a callback function if it matches the arg 41 | * @rtype: the return type of the callback function 42 | * @fn: the callback function to cast 43 | * @arg: the (pointer) argument to hand to the callback function. 44 | * 45 | * If a callback function takes a single argument, this macro does 46 | * appropriate casts to a function which takes a single void * argument if the 47 | * callback provided matches the @arg (or a const or volatile version). 48 | * 49 | * It is assumed that @arg is of pointer type: usually @arg is passed 50 | * or assigned to a void * elsewhere anyway. 51 | * 52 | * Example: 53 | * void _register_callback(void (*fn)(void *arg), void *arg); 54 | * #define register_callback(fn, arg) \ 55 | * _register_callback(typesafe_cb(void, (fn), (arg)), (arg)) 56 | */ 57 | #define typesafe_cb(rtype, fn, arg) \ 58 | cast_if_type(cast_if_type(cast_if_type((fn), \ 59 | rtype (*)(const typeof(*arg)*), \ 60 | rtype (*)(void *)), \ 61 | rtype (*)(volatile typeof(*arg) *), \ 62 | rtype (*)(void *)), \ 63 | rtype (*)(typeof(arg)), \ 64 | rtype (*)(void *)) 65 | 66 | /** 67 | * typesafe_cb_const - cast a const callback function if it matches the arg 68 | * @rtype: the return type of the callback function 69 | * @fn: the callback function to cast 70 | * @arg: the (pointer) argument to hand to the callback function. 71 | * 72 | * If a callback function takes a single argument, this macro does appropriate 73 | * casts to a function which takes a single const void * argument if the 74 | * callback provided matches the @arg. 75 | * 76 | * It is assumed that @arg is of pointer type: usually @arg is passed 77 | * or assigned to a void * elsewhere anyway. 78 | * 79 | * Example: 80 | * void _register_callback(void (*fn)(const void *arg), const void *arg); 81 | * #define register_callback(fn, arg) \ 82 | * _register_callback(typesafe_cb_const(void, (fn), (arg)), (arg)) 83 | */ 84 | #define typesafe_cb_const(rtype, fn, arg) \ 85 | cast_if_type((fn), \ 86 | rtype (*)(const typeof(*arg)*), rtype (*)(const void *)) 87 | 88 | /** 89 | * typesafe_cb_preargs - cast a callback function if it matches the arg 90 | * @rtype: the return type of the callback function 91 | * @fn: the callback function to cast 92 | * @arg: the (pointer) argument to hand to the callback function. 93 | * 94 | * This is a version of typesafe_cb() for callbacks that take other arguments 95 | * before the @arg. 96 | * 97 | * Example: 98 | * void _register_callback(void (*fn)(int, void *arg), void *arg); 99 | * #define register_callback(fn, arg) \ 100 | * _register_callback(typesafe_cb_preargs(void, (fn), (arg), int),\ 101 | * (arg)) 102 | */ 103 | #define typesafe_cb_preargs(rtype, fn, arg, ...) \ 104 | cast_if_type(cast_if_type(cast_if_type((fn), \ 105 | rtype (*)(__VA_ARGS__, \ 106 | const typeof(*arg) *),\ 107 | rtype (*)(__VA_ARGS__, \ 108 | void *)), \ 109 | rtype (*)(__VA_ARGS__, \ 110 | volatile typeof(*arg) *), \ 111 | rtype (*)(__VA_ARGS__, void *)), \ 112 | rtype (*)(__VA_ARGS__, typeof(arg)), \ 113 | rtype (*)(__VA_ARGS__, void *)) 114 | 115 | /** 116 | * typesafe_cb_postargs - cast a callback function if it matches the arg 117 | * @rtype: the return type of the callback function 118 | * @fn: the callback function to cast 119 | * @arg: the (pointer) argument to hand to the callback function. 120 | * 121 | * This is a version of typesafe_cb() for callbacks that take other arguments 122 | * after the @arg. 123 | * 124 | * Example: 125 | * void _register_callback(void (*fn)(void *arg, int), void *arg); 126 | * #define register_callback(fn, arg) \ 127 | * _register_callback(typesafe_cb_preargs(void, (fn), (arg), int),\ 128 | * (arg)) 129 | */ 130 | #define typesafe_cb_postargs(rtype, fn, arg, ...) \ 131 | cast_if_type(cast_if_type(cast_if_type((fn), \ 132 | rtype (*)(const typeof(*arg) *, \ 133 | __VA_ARGS__), \ 134 | rtype (*)(void *, \ 135 | __VA_ARGS__)), \ 136 | rtype (*)(volatile typeof(*arg) *, \ 137 | __VA_ARGS__), \ 138 | rtype (*)(void *, __VA_ARGS__)), \ 139 | rtype (*)(typeof(arg), __VA_ARGS__), \ 140 | rtype (*)(void *, __VA_ARGS__)) 141 | 142 | /** 143 | * typesafe_cb_cmp - cast a compare function if it matches the arg 144 | * @rtype: the return type of the callback function 145 | * @fn: the callback function to cast 146 | * @arg: the (pointer) argument(s) to hand to the compare function. 147 | * 148 | * If a callback function takes two matching-type arguments, this macro does 149 | * appropriate casts to a function which takes two const void * arguments if 150 | * the callback provided takes two a const pointers to @arg. 151 | * 152 | * It is assumed that @arg is of pointer type: usually @arg is passed 153 | * or assigned to a void * elsewhere anyway. 154 | * 155 | * Example: 156 | * void _my_qsort(void *base, size_t nmemb, size_t size, 157 | * int (*cmp)(const void *, const void *)); 158 | * #define my_qsort(base, nmemb, cmpfn) \ 159 | * _my_qsort((base), (nmemb), sizeof(*(base)), \ 160 | * typesafe_cb_cmp(int, (cmpfn), (base)), (arg)) 161 | */ 162 | #define typesafe_cb_cmp(rtype, cmpfn, arg) \ 163 | cast_if_type((cmpfn), \ 164 | rtype (*)(const typeof(*arg)*, const typeof(*arg)*), \ 165 | rtype (*)(const void *, const void *)) 166 | #endif /* CCAN_CAST_IF_TYPE_H */ 167 | -------------------------------------------------------------------------------- /tests/test-int.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | 23 | /*----------------------------------------------------------------------- 24 | * Sample data 25 | */ 26 | 27 | const uint32_t DATA_01[] = { 1 }; 28 | const size_t LENGTH_01 = 1 * sizeof(uint32_t); 29 | 30 | 31 | /*----------------------------------------------------------------------- 32 | * Test cases 33 | */ 34 | 35 | 36 | START_TEST(test_integer_01) 37 | { 38 | push_parser_t *parser; 39 | push_callback_t *callback; 40 | uint32_t *result; 41 | 42 | PUSH_DEBUG_MSG("---\nStarting test_integer_01\n"); 43 | 44 | parser = push_parser_new(); 45 | fail_if(parser == NULL, 46 | "Could not allocate a new push parser"); 47 | 48 | callback = integer_callback_new("integer", NULL, parser); 49 | fail_if(callback == NULL, 50 | "Could not allocate a new integer callback"); 51 | 52 | push_parser_set_callback(parser, callback); 53 | 54 | fail_unless(push_parser_activate(parser, NULL) 55 | == PUSH_INCOMPLETE, 56 | "Could not parse data"); 57 | 58 | fail_unless(push_parser_submit_data 59 | (parser, &DATA_01, LENGTH_01) == PUSH_SUCCESS, 60 | "Could not parse data"); 61 | 62 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 63 | "Shouldn't get parse error at EOF"); 64 | 65 | result = push_parser_result(parser, uint32_t); 66 | 67 | fail_unless(*result == 1, 68 | "Integer doesn't match (got %"PRIu32 69 | ", expected %"PRIu32")", 70 | *result, 1); 71 | 72 | push_parser_free(parser); 73 | } 74 | END_TEST 75 | 76 | 77 | START_TEST(test_integer_02) 78 | { 79 | push_parser_t *parser; 80 | push_callback_t *callback; 81 | uint32_t *result; 82 | 83 | PUSH_DEBUG_MSG("---\nStarting test_integer_02\n"); 84 | 85 | /* 86 | * If we submit the data twice, we should get the same result, 87 | * since we'll ignore any later data. 88 | */ 89 | 90 | parser = push_parser_new(); 91 | fail_if(parser == NULL, 92 | "Could not allocate a new push parser"); 93 | 94 | callback = integer_callback_new("integer", NULL, parser); 95 | fail_if(callback == NULL, 96 | "Could not allocate a new integer callback"); 97 | 98 | push_parser_set_callback(parser, callback); 99 | 100 | fail_unless(push_parser_activate(parser, NULL) 101 | == PUSH_INCOMPLETE, 102 | "Could not parse data"); 103 | 104 | fail_unless(push_parser_submit_data 105 | (parser, &DATA_01, LENGTH_01) == PUSH_SUCCESS, 106 | "Could not parse data"); 107 | 108 | fail_unless(push_parser_submit_data 109 | (parser, &DATA_01, LENGTH_01) == PUSH_SUCCESS, 110 | "Could not parse data"); 111 | 112 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 113 | "Shouldn't get parse error at EOF"); 114 | 115 | result = push_parser_result(parser, uint32_t); 116 | 117 | fail_unless(*result == 1, 118 | "Integer doesn't match (got %"PRIu32 119 | ", expected %"PRIu32")", 120 | *result, 1); 121 | 122 | push_parser_free(parser); 123 | } 124 | END_TEST 125 | 126 | 127 | START_TEST(test_integer_03) 128 | { 129 | push_parser_t *parser; 130 | push_callback_t *callback; 131 | uint32_t *result; 132 | size_t FIRST_CHUNK_SIZE = 3; /* something not divisible by 4 */ 133 | 134 | PUSH_DEBUG_MSG("---\nStarting test_integer_03\n"); 135 | 136 | /* 137 | * If we submit the data in two chunks (that don't align on the 138 | * integer boundaries), we should get the same result. 139 | */ 140 | 141 | parser = push_parser_new(); 142 | fail_if(parser == NULL, 143 | "Could not allocate a new push parser"); 144 | 145 | callback = integer_callback_new("integer", NULL, parser); 146 | fail_if(callback == NULL, 147 | "Could not allocate a new integer callback"); 148 | 149 | push_parser_set_callback(parser, callback); 150 | 151 | fail_unless(push_parser_activate(parser, NULL) 152 | == PUSH_INCOMPLETE, 153 | "Could not parse data"); 154 | 155 | fail_unless(push_parser_submit_data 156 | (parser, &DATA_01, FIRST_CHUNK_SIZE) == PUSH_INCOMPLETE, 157 | "Could not parse data"); 158 | 159 | fail_unless(push_parser_submit_data 160 | (parser, 161 | ((void *) DATA_01) + FIRST_CHUNK_SIZE, 162 | LENGTH_01 - FIRST_CHUNK_SIZE) == PUSH_SUCCESS, 163 | "Could not parse data"); 164 | 165 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 166 | "Shouldn't get parse error at EOF"); 167 | 168 | result = push_parser_result(parser, uint32_t); 169 | 170 | fail_unless(*result == 1, 171 | "Integer doesn't match (got %"PRIu32 172 | ", expected %"PRIu32")", 173 | *result, 1); 174 | 175 | push_parser_free(parser); 176 | } 177 | END_TEST 178 | 179 | 180 | START_TEST(test_parse_error_01) 181 | { 182 | push_parser_t *parser; 183 | push_callback_t *callback; 184 | size_t FIRST_CHUNK_SIZE = 3; /* something not divisible by 4 */ 185 | 186 | PUSH_DEBUG_MSG("---\nStarting test_parse_error_01\n"); 187 | 188 | /* 189 | * Our callback processes integers on nice 32-bit boundaries. If 190 | * we send the parser data that doesn't align with these 191 | * boundaries, and then reach EOF, we should get a parse error, 192 | * since internally the integer callback is wrapped in a 193 | * push_min_bytes_t. 194 | */ 195 | 196 | parser = push_parser_new(); 197 | fail_if(parser == NULL, 198 | "Could not allocate a new push parser"); 199 | 200 | callback = integer_callback_new("integer", NULL, parser); 201 | fail_if(callback == NULL, 202 | "Could not allocate a new integer callback"); 203 | 204 | push_parser_set_callback(parser, callback); 205 | 206 | fail_unless(push_parser_activate(parser, NULL) 207 | == PUSH_INCOMPLETE, 208 | "Could not parse data"); 209 | 210 | fail_unless(push_parser_submit_data 211 | (parser, &DATA_01, FIRST_CHUNK_SIZE) == PUSH_INCOMPLETE, 212 | "Could not parse data"); 213 | 214 | fail_unless(push_parser_eof(parser) == PUSH_PARSE_ERROR, 215 | "Should get parse error at EOF"); 216 | 217 | push_parser_free(parser); 218 | } 219 | END_TEST 220 | 221 | 222 | /*----------------------------------------------------------------------- 223 | * Testing harness 224 | */ 225 | 226 | Suite * 227 | test_suite() 228 | { 229 | Suite *s = suite_create("integer-callback"); 230 | 231 | TCase *tc = tcase_create("integer-callback"); 232 | tcase_add_test(tc, test_integer_01); 233 | tcase_add_test(tc, test_integer_02); 234 | tcase_add_test(tc, test_integer_03); 235 | tcase_add_test(tc, test_parse_error_01); 236 | suite_add_tcase(s, tc); 237 | 238 | return s; 239 | } 240 | 241 | 242 | int 243 | main(int argc, const char **argv) 244 | { 245 | int number_failed; 246 | Suite *suite = test_suite(); 247 | SRunner *runner = srunner_create(suite); 248 | 249 | srunner_run_all(runner, CK_NORMAL); 250 | number_failed = srunner_ntests_failed(runner); 251 | srunner_free(runner); 252 | 253 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 254 | } 255 | -------------------------------------------------------------------------------- /include/push/protobuf/field-map.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PROTOBUF_FIELD_MAP_H 12 | #define PUSH_PROTOBUF_FIELD_MAP_H 13 | 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | 22 | /** 23 | * A map of field numbers to callbacks for reading that field. Used 24 | * within a message callback; once it reads in a field number, it 25 | * dispatches to a callback that can read that field. This map stores 26 | * the list of possible callbacks. 27 | */ 28 | 29 | typedef struct _push_protobuf_field_map push_protobuf_field_map_t; 30 | 31 | 32 | /** 33 | * Create a new field map. The field map should be created and 34 | * populated before creating the message callback that will use it. 35 | */ 36 | 37 | push_protobuf_field_map_t * 38 | push_protobuf_field_map_new(void *parent); 39 | 40 | 41 | /** 42 | * Sets the success continuation pointer for all of the callbacks in 43 | * the field map. 44 | */ 45 | 46 | void 47 | push_protobuf_field_map_set_success 48 | (push_protobuf_field_map_t *field_map, 49 | push_success_continuation_t *success); 50 | 51 | 52 | /** 53 | * Sets the incomplete continuation pointer for all of the callbacks 54 | * in the field map. 55 | */ 56 | 57 | void 58 | push_protobuf_field_map_set_incomplete 59 | (push_protobuf_field_map_t *field_map, 60 | push_incomplete_continuation_t *incomplete); 61 | 62 | 63 | /** 64 | * Sets the error continuation pointer for all of the callbacks in the 65 | * field map. 66 | */ 67 | 68 | void 69 | push_protobuf_field_map_set_error 70 | (push_protobuf_field_map_t *field_map, 71 | push_error_continuation_t *error); 72 | 73 | 74 | /** 75 | * Add a new field to a field map. The value callback will be called 76 | * whenever we read a field with the matching field number. This 77 | * value callback should also store the parsed value away somewhere, 78 | * if needed. We also verify that the tag type matches what we 79 | * expect, throwing a parse error if it doesn't. 80 | * 81 | * @return false if we cannot add the new field. 82 | */ 83 | 84 | bool 85 | push_protobuf_field_map_add_field 86 | (const char *field_name, 87 | push_parser_t *parser, 88 | push_protobuf_field_map_t *field_map, 89 | push_protobuf_tag_number_t field_number, 90 | push_protobuf_tag_type_t expected_tag_type, 91 | push_callback_t *value_callback); 92 | 93 | 94 | /** 95 | * Get the field callback for the specified field. If that field 96 | * isn't in the field map, return NULL. 97 | */ 98 | 99 | push_callback_t * 100 | push_protobuf_field_map_get_field 101 | (push_protobuf_field_map_t *field_map, 102 | push_protobuf_tag_number_t field_number); 103 | 104 | 105 | /** 106 | * Add a new submessage to a field map. 107 | * 108 | * @return false if we cannot add the new field. 109 | */ 110 | 111 | bool 112 | push_protobuf_add_submessage(const char *message_name, 113 | const char *field_name, 114 | void *parent, 115 | push_parser_t *parser, 116 | push_protobuf_field_map_t *field_map, 117 | push_protobuf_tag_number_t field_number, 118 | push_callback_t *message); 119 | 120 | 121 | /** 122 | * Create a new callback that reads a length-prefixed Protocol Buffer 123 | * string into a high-water mark buffer. 124 | */ 125 | 126 | bool 127 | push_protobuf_add_hwm_string(const char *message_name, 128 | const char *field_name, 129 | void *parent, 130 | push_parser_t *parser, 131 | push_protobuf_field_map_t *field_map, 132 | push_protobuf_tag_number_t field_number, 133 | hwm_buffer_t *dest); 134 | 135 | 136 | /** 137 | * Add a new uint32 field to a field map. When parsing, 138 | * the field's value will be assigned to the dest pointer. 139 | * 140 | * @return false if we cannot add the new field. 141 | */ 142 | 143 | bool 144 | push_protobuf_assign_uint32(const char *message_name, 145 | const char *field_name, 146 | void *parent, 147 | push_parser_t *parser, 148 | push_protobuf_field_map_t *field_map, 149 | push_protobuf_tag_number_t field_number, 150 | uint32_t *dest); 151 | 152 | 153 | /** 154 | * Add a new uint64 field to a field map. When parsing, 155 | * the field's value will be assigned to the dest pointer. 156 | * 157 | * @return false if we cannot add the new field. 158 | */ 159 | 160 | bool 161 | push_protobuf_assign_uint64(const char *message_name, 162 | const char *field_name, 163 | void *parent, 164 | push_parser_t *parser, 165 | push_protobuf_field_map_t *field_map, 166 | push_protobuf_tag_number_t field_number, 167 | uint64_t *dest); 168 | 169 | 170 | /** 171 | * Add a new int32 field to a field map. When parsing, 172 | * the field's value will be assigned to the dest pointer. 173 | * 174 | * @return false if we cannot add the new field. 175 | */ 176 | 177 | bool 178 | push_protobuf_assign_int32(const char *message_name, 179 | const char *field_name, 180 | void *parent, 181 | push_parser_t *parser, 182 | push_protobuf_field_map_t *field_map, 183 | push_protobuf_tag_number_t field_number, 184 | int32_t *dest); 185 | 186 | 187 | /** 188 | * Add a new int64 field to a field map. When parsing, 189 | * the field's value will be assigned to the dest pointer. 190 | * 191 | * @return false if we cannot add the new field. 192 | */ 193 | 194 | bool 195 | push_protobuf_assign_int64(const char *message_name, 196 | const char *field_name, 197 | void *parent, 198 | push_parser_t *parser, 199 | push_protobuf_field_map_t *field_map, 200 | push_protobuf_tag_number_t field_number, 201 | int64_t *dest); 202 | 203 | 204 | /** 205 | * Add a new sint32 field to a field map. When parsing, 206 | * the field's value will be assigned to the dest pointer. 207 | * 208 | * @return false if we cannot add the new field. 209 | */ 210 | 211 | bool 212 | push_protobuf_assign_sint32(const char *message_name, 213 | const char *field_name, 214 | void *parent, 215 | push_parser_t *parser, 216 | push_protobuf_field_map_t *field_map, 217 | push_protobuf_tag_number_t field_number, 218 | int32_t *dest); 219 | 220 | 221 | /** 222 | * Add a new sint64 field to a field map. When parsing, 223 | * the field's value will be assigned to the dest pointer. 224 | * 225 | * @return false if we cannot add the new field. 226 | */ 227 | 228 | bool 229 | push_protobuf_assign_sint64(const char *message_name, 230 | const char *field_name, 231 | void *parent, 232 | push_parser_t *parser, 233 | push_protobuf_field_map_t *field_map, 234 | push_protobuf_tag_number_t field_number, 235 | int64_t *dest); 236 | 237 | 238 | #endif /* PUSH_PROTOBUF_FIELD_MAP_H */ 239 | -------------------------------------------------------------------------------- /include/push/pure.h: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #ifndef PUSH_PURE_H 12 | #define PUSH_PURE_H 13 | 14 | #include 15 | #include 16 | 17 | /** 18 | * @file 19 | * 20 | * This file defines a macro for creating parser callbacks from pure C 21 | * functions. 22 | */ 23 | 24 | 25 | #define push_define_pure_data_callback(new_func, pure_func, \ 26 | default_name, \ 27 | input_t, \ 28 | output_t, \ 29 | user_data_t) \ 30 | typedef struct _##new_func \ 31 | { \ 32 | push_callback_t callback; \ 33 | user_data_t user_data; \ 34 | } new_func##_t; \ 35 | \ 36 | static void \ 37 | new_func##_activate(void *ud, void *vinput, \ 38 | const void *buf, \ 39 | size_t bytes_remaining) \ 40 | { \ 41 | new_func##_t *pure = (new_func##_t *) ud; \ 42 | input_t *input = (input_t *) vinput; \ 43 | output_t *output; \ 44 | \ 45 | if (pure_func(&pure->user_data, input, &output)) \ 46 | { \ 47 | push_continuation_call(pure->callback.success, \ 48 | output, \ 49 | buf, bytes_remaining); \ 50 | return; \ 51 | } else { \ 52 | push_continuation_call(pure->callback.error, \ 53 | PUSH_PARSE_ERROR, \ 54 | "Pure function failed"); \ 55 | return; \ 56 | } \ 57 | } \ 58 | \ 59 | static push_callback_t * \ 60 | new_func(const char *name, \ 61 | void *parent, \ 62 | push_parser_t *parser, \ 63 | user_data_t **user_data) \ 64 | { \ 65 | new_func##_t *pure; \ 66 | \ 67 | pure = push_talloc(parent, new_func##_t); \ 68 | if (pure == NULL) return NULL; \ 69 | \ 70 | if (user_data != NULL) \ 71 | *user_data = &pure->user_data; \ 72 | \ 73 | if (name == NULL) name = default_name; \ 74 | push_talloc_set_name_const(pure, name); \ 75 | \ 76 | push_callback_init(&pure->callback, parser, pure, \ 77 | new_func##_activate, \ 78 | NULL, NULL, NULL); \ 79 | \ 80 | return &pure->callback; \ 81 | } 82 | 83 | 84 | #define push_define_pure_callback(new_func, pure_func, \ 85 | default_name, \ 86 | input_t, \ 87 | output_t, \ 88 | user_data_t) \ 89 | typedef struct _##new_func \ 90 | { \ 91 | push_callback_t callback; \ 92 | user_data_t *user_data; \ 93 | } new_func##_t; \ 94 | \ 95 | static void \ 96 | new_func##_activate(void *ud, void *vinput, \ 97 | const void *buf, \ 98 | size_t bytes_remaining) \ 99 | { \ 100 | new_func##_t *pure = (new_func##_t *) ud; \ 101 | input_t *input = (input_t *) vinput; \ 102 | output_t *output; \ 103 | \ 104 | if (pure_func(pure->user_data, input, &output)) \ 105 | { \ 106 | push_continuation_call(pure->callback.success, \ 107 | output, \ 108 | buf, bytes_remaining); \ 109 | return; \ 110 | } else { \ 111 | push_continuation_call(pure->callback.error, \ 112 | PUSH_PARSE_ERROR, \ 113 | "Pure function failed"); \ 114 | return; \ 115 | } \ 116 | } \ 117 | \ 118 | static push_callback_t * \ 119 | new_func(const char *name, \ 120 | void *parent, \ 121 | push_parser_t *parser, \ 122 | user_data_t *user_data) \ 123 | { \ 124 | new_func##_t *pure; \ 125 | \ 126 | pure = push_talloc(parent, new_func##_t); \ 127 | if (pure == NULL) return NULL; \ 128 | \ 129 | if (name == NULL) name = default_name; \ 130 | push_talloc_set_name_const(pure, name); \ 131 | \ 132 | push_callback_init(&pure->callback, parser, pure, \ 133 | new_func##_activate, \ 134 | NULL, NULL, NULL); \ 135 | \ 136 | pure->user_data = user_data; \ 137 | \ 138 | return &pure->callback; \ 139 | } 140 | 141 | 142 | #endif /* PUSH_PURE_H */ 143 | -------------------------------------------------------------------------------- /src/protobuf/message.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | /*----------------------------------------------------------------------- 25 | * Dispatch callback 26 | */ 27 | 28 | /** 29 | * A callback that takes in a field tag as input, and then dispatches 30 | * to a reader callback for that field. The readers are stored in a 31 | * “field map”, which is an expandable array of 32 | * push_protobuf_message_field_map_t instances. 33 | */ 34 | 35 | typedef struct _dispatch 36 | { 37 | /** 38 | * The push_callback_t superclass for this callback. 39 | */ 40 | 41 | push_callback_t callback; 42 | 43 | /** 44 | * A mapping of field numbers to the callback that reads the 45 | * corresponding field. 46 | */ 47 | 48 | push_protobuf_field_map_t *field_map; 49 | 50 | /** 51 | * A callback that can skip unknown length-prefixed fields. 52 | */ 53 | 54 | push_callback_t *skip_length_prefixed; 55 | 56 | } dispatch_t; 57 | 58 | 59 | static void 60 | dispatch_set_success(void *user_data, 61 | push_success_continuation_t *success) 62 | { 63 | dispatch_t *dispatch = (dispatch_t *) user_data; 64 | 65 | push_protobuf_field_map_set_success(dispatch->field_map, 66 | success); 67 | 68 | push_continuation_call(&dispatch->skip_length_prefixed 69 | ->set_success, 70 | success); 71 | } 72 | 73 | 74 | static void 75 | dispatch_set_incomplete(void *user_data, 76 | push_incomplete_continuation_t *incomplete) 77 | { 78 | dispatch_t *dispatch = (dispatch_t *) user_data; 79 | 80 | push_protobuf_field_map_set_incomplete(dispatch->field_map, 81 | incomplete); 82 | 83 | push_continuation_call(&dispatch->skip_length_prefixed 84 | ->set_incomplete, 85 | incomplete); 86 | } 87 | 88 | 89 | static void 90 | dispatch_set_error(void *user_data, 91 | push_error_continuation_t *error) 92 | { 93 | dispatch_t *dispatch = (dispatch_t *) user_data; 94 | 95 | dispatch->callback.error = error; 96 | 97 | push_protobuf_field_map_set_error(dispatch->field_map, 98 | error); 99 | 100 | push_continuation_call(&dispatch->skip_length_prefixed 101 | ->set_error, 102 | error); 103 | } 104 | 105 | 106 | static void 107 | dispatch_activate(void *user_data, 108 | void *result, 109 | const void *buf, 110 | size_t bytes_remaining) 111 | { 112 | dispatch_t *dispatch = (dispatch_t *) user_data; 113 | push_protobuf_tag_t *field_tag; 114 | push_protobuf_tag_number_t field_number; 115 | push_callback_t *field_callback; 116 | 117 | field_tag = (push_protobuf_tag_t *) result; 118 | PUSH_DEBUG_MSG("%s: Activating. Got tag 0x%04"PRIx32"\n", 119 | push_talloc_get_name(dispatch), 120 | *field_tag); 121 | 122 | /* 123 | * Extract the field number from the tag_callback. 124 | */ 125 | 126 | field_number = PUSH_PROTOBUF_GET_TAG_NUMBER(*field_tag); 127 | 128 | PUSH_DEBUG_MSG("%s: Dispatching field %"PRIu32".\n", 129 | push_talloc_get_name(dispatch), 130 | field_number); 131 | 132 | /* 133 | * Get the field callback for this field number. 134 | */ 135 | 136 | field_callback = 137 | push_protobuf_field_map_get_field(dispatch->field_map, 138 | field_number); 139 | 140 | if (field_callback == NULL) 141 | { 142 | push_protobuf_tag_type_t field_type = 143 | PUSH_PROTOBUF_GET_TAG_TYPE(*field_tag); 144 | 145 | switch (field_type) 146 | { 147 | case PUSH_PROTOBUF_TAG_TYPE_LENGTH_DELIMITED: 148 | field_callback = dispatch->skip_length_prefixed; 149 | break; 150 | 151 | default: 152 | /* 153 | * TODO: Add skippers for the other field types. 154 | */ 155 | 156 | PUSH_DEBUG_MSG("%s: No field callback for " 157 | "field %"PRIu32".\n", 158 | push_talloc_get_name(dispatch), 159 | field_number); 160 | 161 | push_continuation_call(dispatch->callback.error, 162 | PUSH_PARSE_ERROR, 163 | "No callback for field"); 164 | 165 | return; 166 | } 167 | } 168 | 169 | /* 170 | * Found it! Activate that callback we just found. The field 171 | * callback is going to need to verify the wire type, so make sure 172 | * to pass in the tag as input. 173 | */ 174 | 175 | PUSH_DEBUG_MSG("%s: Callback %p matches.\n", 176 | push_talloc_get_name(dispatch), 177 | field_callback); 178 | 179 | push_continuation_call(&field_callback->activate, 180 | field_tag, 181 | buf, bytes_remaining); 182 | 183 | return; 184 | 185 | } 186 | 187 | 188 | static push_callback_t * 189 | dispatch_new(const char *name, 190 | void *parent, 191 | push_parser_t *parser, 192 | push_protobuf_field_map_t *field_map) 193 | { 194 | void *context; 195 | dispatch_t *dispatch = NULL; 196 | push_callback_t *skip_length_prefixed = NULL; 197 | 198 | /* 199 | * If the field map is NULL, return NULL ourselves. 200 | */ 201 | 202 | if (field_map == NULL) 203 | return NULL; 204 | 205 | /* 206 | * Create a memory context for the objects we're about to create. 207 | */ 208 | 209 | context = push_talloc_new(parent); 210 | if (context == NULL) return NULL; 211 | 212 | /* 213 | * Next, allocate the dispatch callback itself. 214 | */ 215 | 216 | dispatch = push_talloc(context, dispatch_t); 217 | if (dispatch == NULL) goto error; 218 | 219 | push_talloc_set_name_const(dispatch, name); 220 | 221 | /* 222 | * Then try to create the skipper callbacks. 223 | */ 224 | 225 | skip_length_prefixed = 226 | push_protobuf_skip_length_prefixed_new 227 | (push_talloc_asprintf(context, "%s.skip-length-prefixed", name), 228 | context, parser); 229 | if (skip_length_prefixed == NULL) goto error; 230 | 231 | /* 232 | * Make the field map a child of the dispatch callback. 233 | */ 234 | 235 | push_talloc_steal(dispatch, field_map); 236 | 237 | /* 238 | * Fill in the data items. 239 | */ 240 | 241 | dispatch->field_map = field_map; 242 | dispatch->skip_length_prefixed = skip_length_prefixed; 243 | 244 | /* 245 | * Initialize the push_callback_t instance. 246 | */ 247 | 248 | push_callback_init(&dispatch->callback, parser, dispatch, 249 | dispatch_activate, 250 | dispatch_set_success, 251 | dispatch_set_incomplete, 252 | dispatch_set_error); 253 | 254 | return &dispatch->callback; 255 | 256 | error: 257 | /* 258 | * Before returning, free any objects we created before the error. 259 | */ 260 | 261 | push_talloc_free(context); 262 | return NULL; 263 | } 264 | 265 | 266 | /*----------------------------------------------------------------------- 267 | * Top-level message callback 268 | */ 269 | 270 | 271 | push_callback_t * 272 | push_protobuf_message_new(const char *name, 273 | void *parent, 274 | push_parser_t *parser, 275 | push_protobuf_field_map_t *field_map) 276 | { 277 | void *context; 278 | push_callback_t *read_field_tag; 279 | push_callback_t *dispatch; 280 | push_callback_t *compose; 281 | push_callback_t *fold; 282 | 283 | /* 284 | * If the field map is NULL, return NULL ourselves. 285 | */ 286 | 287 | if (field_map == NULL) 288 | return NULL; 289 | 290 | /* 291 | * Create a memory context for the objects we're about to create. 292 | */ 293 | 294 | context = push_talloc_new(parent); 295 | if (context == NULL) return NULL; 296 | 297 | /* 298 | * Create the callbacks. 299 | */ 300 | 301 | if (name == NULL) name = "message"; 302 | 303 | read_field_tag = push_protobuf_varint32_new 304 | (push_talloc_asprintf(context, "%s.tag", name), 305 | context, parser); 306 | dispatch = dispatch_new 307 | (push_talloc_asprintf(context, "%s.dispatch", name), 308 | context, parser, field_map); 309 | compose = push_compose_new 310 | (push_talloc_asprintf(context, "%s.compose", name), 311 | context, parser, 312 | read_field_tag, dispatch); 313 | fold = push_fold_new 314 | (push_talloc_asprintf(context, "%s.fold", name), 315 | context, parser, compose); 316 | 317 | /* 318 | * Because of NULL propagation, we only have to check the last 319 | * result to see if everything was created okay. 320 | */ 321 | 322 | if (fold == NULL) goto error; 323 | return fold; 324 | 325 | error: 326 | /* 327 | * Before returning, free any objects we created before the error. 328 | */ 329 | 330 | push_talloc_free(context); 331 | return NULL; 332 | } 333 | -------------------------------------------------------------------------------- /src/hwm-string.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | 22 | /** 23 | * The user data struct for an HWM-string callback. 24 | */ 25 | 26 | typedef struct _hwm_string 27 | { 28 | /** 29 | * The push_callback_t superclass for this callback. 30 | */ 31 | 32 | push_callback_t callback; 33 | 34 | /** 35 | * The continue continuation for this callback. 36 | */ 37 | 38 | push_continue_continuation_t cont; 39 | 40 | /** 41 | * A pointer to the HWM buffer that we'll put the string into. 42 | */ 43 | 44 | hwm_buffer_t *buf; 45 | 46 | /** 47 | * The number of bytes left to add to the HWM buffer. 48 | */ 49 | 50 | size_t bytes_left; 51 | 52 | } hwm_string_t; 53 | 54 | 55 | static void 56 | hwm_string_continue(void *user_data, 57 | const void *buf, 58 | size_t bytes_remaining) 59 | { 60 | hwm_string_t *hwm_string = (hwm_string_t *) user_data; 61 | size_t bytes_to_copy; 62 | 63 | /* 64 | * EOF is a parse error if we haven't read in all of the string 65 | * yet. 66 | */ 67 | 68 | if (bytes_remaining == 0) 69 | { 70 | if (hwm_string->bytes_left == 0) 71 | { 72 | void *str; 73 | 74 | /* 75 | * In most cases, we'll have already returned PUSH_SUCCESS 76 | * when we actually read in the data, but we'll find 77 | * ourselves here if we're asked to read in a 0-byte 78 | * string that occurs right at the end of the stream. 79 | */ 80 | 81 | PUSH_DEBUG_MSG("%s: EOF found at end of string. " 82 | "Parse successful.\n", 83 | push_talloc_get_name(hwm_string)); 84 | 85 | /* 86 | * Get a pointer to the HWM buffer's contents. 87 | */ 88 | 89 | str = hwm_buffer_writable_mem(hwm_string->buf, void); 90 | if (str == NULL) 91 | { 92 | PUSH_DEBUG_MSG("%s: Cannot get pointer to buffer.\n", 93 | push_talloc_get_name(hwm_string)); 94 | 95 | push_continuation_call(hwm_string->callback.error, 96 | PUSH_MEMORY_ERROR, 97 | "Cannot get pointer to buffer"); 98 | 99 | return; 100 | } 101 | 102 | /* 103 | * Our result is a pointer to the HWM buffer's contents. 104 | */ 105 | 106 | push_continuation_call(hwm_string->callback.success, 107 | str, 108 | buf, bytes_remaining); 109 | 110 | return; 111 | 112 | } else { 113 | PUSH_DEBUG_MSG("%s: EOF found before end of string. " 114 | "Parse fails.\n", 115 | push_talloc_get_name(hwm_string)); 116 | 117 | push_continuation_call(hwm_string->callback.error, 118 | PUSH_PARSE_ERROR, 119 | "EOF found before end of string"); 120 | 121 | return; 122 | } 123 | } 124 | 125 | /* 126 | * Make sure we don't copy more data than is available, or more 127 | * data than we need. 128 | */ 129 | 130 | bytes_to_copy = 131 | (bytes_remaining < hwm_string->bytes_left)? 132 | bytes_remaining: 133 | hwm_string->bytes_left; 134 | 135 | PUSH_DEBUG_MSG("%s: Copying %zu bytes into buffer.\n", 136 | push_talloc_get_name(hwm_string), 137 | bytes_to_copy); 138 | 139 | /* 140 | * Append this chunk of data to the buffer. 141 | */ 142 | 143 | if (!hwm_buffer_append_mem(hwm_string->buf, buf, bytes_to_copy)) 144 | { 145 | PUSH_DEBUG_MSG("%s: Copying failed.\n", 146 | push_talloc_get_name(hwm_string)); 147 | 148 | push_continuation_call(hwm_string->callback.error, 149 | PUSH_MEMORY_ERROR, 150 | "Copying failed"); 151 | 152 | return; 153 | } 154 | 155 | /* 156 | * If that's the end of the string, append a NUL terminator and 157 | * return a success code. 158 | */ 159 | 160 | hwm_string->bytes_left -= bytes_to_copy; 161 | buf += bytes_to_copy; 162 | bytes_remaining -= bytes_to_copy; 163 | 164 | if (hwm_string->bytes_left == 0) 165 | { 166 | uint8_t *str; 167 | 168 | PUSH_DEBUG_MSG("%s: Copying finished. Appending " 169 | "NUL terminator.\n", 170 | push_talloc_get_name(hwm_string)); 171 | 172 | /* 173 | * Get a pointer to the HWM buffer's contents. 174 | */ 175 | 176 | str = hwm_buffer_writable_mem(hwm_string->buf, uint8_t); 177 | if (str == NULL) 178 | { 179 | PUSH_DEBUG_MSG("%s: Cannot get pointer to buffer.\n", 180 | push_talloc_get_name(hwm_string)); 181 | 182 | push_continuation_call(hwm_string->callback.error, 183 | PUSH_MEMORY_ERROR, 184 | "Cannot get pointer to buffer"); 185 | 186 | return; 187 | } 188 | 189 | /* 190 | * Tack on a NUL terminator. 191 | */ 192 | 193 | str[hwm_string->buf->current_size] = '\0'; 194 | hwm_string->buf->current_size++; 195 | 196 | /* 197 | * Our result is the pointer to the buffer contents. 198 | */ 199 | 200 | 201 | push_continuation_call(hwm_string->callback.success, 202 | str, 203 | buf, bytes_remaining); 204 | 205 | return; 206 | } 207 | 208 | /* 209 | * If there's more string to copy, return an incomplete code. 210 | */ 211 | 212 | push_continuation_call(hwm_string->callback.incomplete, 213 | &hwm_string->cont); 214 | } 215 | 216 | 217 | static void 218 | hwm_string_activate(void *user_data, 219 | void *result, 220 | const void *buf, 221 | size_t bytes_remaining) 222 | { 223 | hwm_string_t *hwm_string = (hwm_string_t *) user_data; 224 | size_t *input_size = (size_t *) result; 225 | 226 | PUSH_DEBUG_MSG("%s: Activating. Will read %zu bytes.\n", 227 | push_talloc_get_name(hwm_string), 228 | *input_size); 229 | 230 | /* 231 | * Initialize the callback's fields. 232 | */ 233 | 234 | hwm_string->bytes_left = *input_size; 235 | 236 | if (!hwm_buffer_clear(hwm_string->buf)) 237 | { 238 | PUSH_DEBUG_MSG("%s: Cannot clear HWM buffer.\n", 239 | push_talloc_get_name(hwm_string)); 240 | 241 | push_continuation_call(hwm_string->callback.error, 242 | PUSH_MEMORY_ERROR, 243 | "Cannot clear HWM buffer"); 244 | 245 | return; 246 | } 247 | 248 | /* 249 | * Since we know in advance how big the string will need to be, 250 | * preallocate enough space for it. Include an extra byte for the 251 | * NUL terminator. 252 | */ 253 | 254 | if (hwm_buffer_ensure_size(hwm_string->buf, (*input_size) + 1)) 255 | { 256 | PUSH_DEBUG_MSG("%s: Successfully allocated %zu bytes.\n", 257 | push_talloc_get_name(hwm_string), 258 | (*input_size) + 1); 259 | } else { 260 | PUSH_DEBUG_MSG("%s: Could not allocate %zu bytes.\n", 261 | push_talloc_get_name(hwm_string), 262 | (*input_size) + 1); 263 | 264 | push_continuation_call(hwm_string->callback.error, 265 | PUSH_MEMORY_ERROR, 266 | "Could not allocate HWM buffer"); 267 | 268 | return; 269 | } 270 | 271 | 272 | if (bytes_remaining == 0) 273 | { 274 | /* 275 | * If we don't get any data when we're activated, return an 276 | * incomplete and wait for some data. 277 | */ 278 | 279 | push_continuation_call(hwm_string->callback.incomplete, 280 | &hwm_string->cont); 281 | 282 | return; 283 | 284 | } else { 285 | /* 286 | * Otherwise let the continue continuation go ahead and 287 | * process this chunk of data. 288 | */ 289 | 290 | hwm_string_continue(user_data, buf, bytes_remaining); 291 | return; 292 | } 293 | } 294 | 295 | 296 | push_callback_t * 297 | push_hwm_string_new(const char *name, 298 | void *parent, 299 | push_parser_t *parser, 300 | hwm_buffer_t *buf) 301 | { 302 | hwm_string_t *hwm_string = push_talloc(parent, hwm_string_t); 303 | 304 | if (hwm_string == NULL) 305 | return NULL; 306 | 307 | /* 308 | * Fill in the data items. 309 | */ 310 | 311 | hwm_string->buf = buf; 312 | 313 | /* 314 | * Initialize the push_callback_t instance. 315 | */ 316 | 317 | if (name == NULL) name = "hwm-string"; 318 | push_talloc_set_name_const(hwm_string, name); 319 | 320 | push_callback_init(&hwm_string->callback, parser, hwm_string, 321 | hwm_string_activate, 322 | NULL, NULL, NULL); 323 | 324 | /* 325 | * Fill in the continuation objects for the continuations that we 326 | * implement. 327 | */ 328 | 329 | push_continuation_set(&hwm_string->cont, 330 | hwm_string_continue, 331 | hwm_string); 332 | 333 | return &hwm_string->callback; 334 | } 335 | -------------------------------------------------------------------------------- /src/protobuf/assign.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | 27 | static bool 28 | assign_uint32(uint32_t *dest, uint32_t *input, uint32_t **output) 29 | { 30 | *dest = *input; 31 | *output = dest; 32 | return true; 33 | } 34 | 35 | push_define_pure_callback(assign_uint32_new, assign_uint32, "assign", 36 | uint32_t, uint32_t, uint32_t); 37 | 38 | 39 | 40 | static bool 41 | assign_uint64(uint64_t *dest, uint64_t *input, uint64_t **output) 42 | { 43 | *dest = *input; 44 | *output = dest; 45 | return true; 46 | } 47 | 48 | push_define_pure_callback(assign_uint64_new, assign_uint64, "assign", 49 | uint64_t, uint64_t, uint64_t); 50 | 51 | 52 | 53 | static bool 54 | assign_int32(int32_t *dest, uint32_t *input, int32_t **output) 55 | { 56 | *dest = *input; 57 | *output = dest; 58 | return true; 59 | } 60 | 61 | push_define_pure_callback(assign_int32_new, assign_int32, "assign", 62 | uint32_t, int32_t, int32_t); 63 | 64 | 65 | 66 | static bool 67 | assign_int64(int64_t *dest, uint64_t *input, int64_t **output) 68 | { 69 | *dest = *input; 70 | *output = dest; 71 | return true; 72 | } 73 | 74 | push_define_pure_callback(assign_int64_new, assign_int64, "assign", 75 | uint64_t, int64_t, int64_t); 76 | 77 | 78 | 79 | static bool 80 | assign_sint32(int32_t *dest, uint32_t *input, int32_t **output) 81 | { 82 | *dest = PUSH_PROTOBUF_ZIGZAG_DECODE32(*input); 83 | *output = dest; 84 | return true; 85 | } 86 | 87 | push_define_pure_callback(assign_sint32_new, assign_sint32, "assign", 88 | uint32_t, int32_t, int32_t); 89 | 90 | 91 | 92 | static bool 93 | assign_sint64(int64_t *dest, uint64_t *input, int64_t **output) 94 | { 95 | *dest = PUSH_PROTOBUF_ZIGZAG_DECODE64(*input); 96 | *output = dest; 97 | return true; 98 | } 99 | 100 | push_define_pure_callback(assign_sint64_new, assign_sint64, "assign", 101 | uint64_t, int64_t, int64_t); 102 | 103 | 104 | 105 | #define ADD_FIELD(ASSIGN, VALUE_STR, VALUE_CALLBACK_NEW, \ 106 | DEST_STR, DEST_T, ASSIGN_NEW, \ 107 | TAG_TYPE) \ 108 | bool \ 109 | ASSIGN(const char *message_name, \ 110 | const char *field_name, \ 111 | void *parent, \ 112 | push_parser_t *parser, \ 113 | push_protobuf_field_map_t *field_map, \ 114 | push_protobuf_tag_number_t field_number, \ 115 | DEST_T *dest) \ 116 | { \ 117 | void *context; \ 118 | const char *full_field_name; \ 119 | push_callback_t *value; \ 120 | push_callback_t *assign; \ 121 | push_callback_t *field; \ 122 | \ 123 | /* \ 124 | * If the field map is NULL, return false. \ 125 | */ \ 126 | \ 127 | if (field_map == NULL) \ 128 | return false; \ 129 | \ 130 | /* \ 131 | * Create a memory context for the objects we're about to create. \ 132 | */ \ 133 | \ 134 | context = push_talloc_new(parent); \ 135 | if (context == NULL) return NULL; \ 136 | \ 137 | /* \ 138 | * Create the callbacks. \ 139 | */ \ 140 | \ 141 | if (message_name == NULL) message_name = "message"; \ 142 | if (field_name == NULL) field_name = ".assign"; \ 143 | \ 144 | full_field_name = \ 145 | push_talloc_asprintf(context, "%s.%s", \ 146 | message_name, field_name); \ 147 | \ 148 | value = VALUE_CALLBACK_NEW \ 149 | (push_talloc_asprintf(context, "%s." VALUE_STR, \ 150 | full_field_name), \ 151 | context, parser); \ 152 | assign = ASSIGN_NEW \ 153 | (push_talloc_asprintf(context, "%s." DEST_STR, \ 154 | full_field_name), \ 155 | context, parser, \ 156 | dest); \ 157 | field = push_compose_new \ 158 | (push_talloc_asprintf(context, "%s.compose", \ 159 | full_field_name), \ 160 | context, parser, \ 161 | value, assign); \ 162 | \ 163 | /* \ 164 | * Because of NULL propagation, we only have to check the last \ 165 | * result to see if everything was created okay. \ 166 | */ \ 167 | \ 168 | if (field == NULL) goto error; \ 169 | \ 170 | /* \ 171 | * Try to add the new field. If we can't, free the field before \ 172 | * returning. \ 173 | */ \ 174 | \ 175 | if (!push_protobuf_field_map_add_field \ 176 | (full_field_name, parser, \ 177 | field_map, field_number, TAG_TYPE, field)) \ 178 | { \ 179 | goto error; \ 180 | } \ 181 | \ 182 | return true; \ 183 | \ 184 | error: \ 185 | /* \ 186 | * Before returning, free any objects we created before the error. \ 187 | */ \ 188 | \ 189 | push_talloc_free(context); \ 190 | return false; \ 191 | } 192 | 193 | 194 | ADD_FIELD(push_protobuf_assign_uint32, 195 | "varint32", push_protobuf_varint32_new, 196 | "uint32", uint32_t, assign_uint32_new, 197 | PUSH_PROTOBUF_TAG_TYPE_VARINT); 198 | 199 | 200 | ADD_FIELD(push_protobuf_assign_uint64, 201 | "varint64", push_protobuf_varint64_new, 202 | "uint64", uint64_t, assign_uint64_new, 203 | PUSH_PROTOBUF_TAG_TYPE_VARINT); 204 | 205 | 206 | ADD_FIELD(push_protobuf_assign_int32, 207 | "varint32", push_protobuf_varint32_new, 208 | "int32", int32_t, assign_int32_new, 209 | PUSH_PROTOBUF_TAG_TYPE_VARINT); 210 | 211 | 212 | ADD_FIELD(push_protobuf_assign_int64, 213 | "varint64", push_protobuf_varint64_new, 214 | "int64", int64_t, assign_int64_new, 215 | PUSH_PROTOBUF_TAG_TYPE_VARINT); 216 | 217 | 218 | ADD_FIELD(push_protobuf_assign_sint32, 219 | "varint32", push_protobuf_varint32_new, 220 | "int32", int32_t, assign_sint32_new, 221 | PUSH_PROTOBUF_TAG_TYPE_VARINT); 222 | 223 | 224 | ADD_FIELD(push_protobuf_assign_sint64, 225 | "varint64", push_protobuf_varint64_new, 226 | "int64", int64_t, assign_sint64_new, 227 | PUSH_PROTOBUF_TAG_TYPE_VARINT); 228 | -------------------------------------------------------------------------------- /tests/test-double-sum.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2009-2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | 27 | /*----------------------------------------------------------------------- 28 | * Double-sum callback implementation 29 | * 30 | * This callbackcreates two sums from a list of integers — the first 31 | * is the sum of all of the even-indexed entries, the second the sum 32 | * of the odd-indexed entries. We can do this by creating a par of 33 | * two regular sum callbacks. 34 | */ 35 | 36 | static push_callback_t * 37 | make_double_sum_callback(push_parser_t *parser) 38 | { 39 | void *context; 40 | push_callback_t *sum1; 41 | push_callback_t *sum2; 42 | push_callback_t *par; 43 | push_callback_t *fold; 44 | 45 | context = push_talloc_new(NULL); 46 | if (context == NULL) return NULL; 47 | 48 | sum1 = sum_callback_new 49 | ("sum1", context, parser); 50 | sum2 = sum_callback_new 51 | ("sum2", context, parser); 52 | par = push_par_new 53 | ("par", context, parser, sum1, sum2); 54 | fold = push_fold_new 55 | ("fold", context, parser, par); 56 | 57 | if (fold == NULL) goto error; 58 | return fold; 59 | 60 | error: 61 | push_talloc_free(context); 62 | return NULL; 63 | } 64 | 65 | 66 | /*----------------------------------------------------------------------- 67 | * Sample data 68 | */ 69 | 70 | uint32_t INT_0 = 0; 71 | push_pair_t PAIR_0 = { &INT_0, &INT_0 }; 72 | 73 | const uint32_t DATA_01[] = { 1, 2, 3, 4, 5, 6 }; 74 | const size_t LENGTH_01 = 6 * sizeof(uint32_t); 75 | 76 | 77 | /*----------------------------------------------------------------------- 78 | * Test cases 79 | */ 80 | 81 | 82 | START_TEST(test_double_sum_01) 83 | { 84 | push_parser_t *parser; 85 | push_callback_t *callback; 86 | push_pair_t *result; 87 | uint32_t *sum1; 88 | uint32_t *sum2; 89 | 90 | PUSH_DEBUG_MSG("---\nStarting test_double_sum_01\n"); 91 | 92 | parser = push_parser_new(); 93 | fail_if(parser == NULL, 94 | "Could not allocate a new push parser"); 95 | 96 | callback = make_double_sum_callback(parser); 97 | fail_if(callback == NULL, 98 | "Could not allocate double-sum callback"); 99 | 100 | push_parser_set_callback(parser, callback); 101 | 102 | fail_unless(push_parser_activate(parser, &PAIR_0) 103 | == PUSH_INCOMPLETE, 104 | "Could not activate parser"); 105 | 106 | fail_unless(push_parser_submit_data 107 | (parser, &DATA_01, LENGTH_01) == PUSH_INCOMPLETE, 108 | "Could not parse data"); 109 | 110 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 111 | "Shouldn't get parse error at EOF"); 112 | 113 | result = push_parser_result(parser, push_pair_t); 114 | sum1 = (uint32_t *) result->first; 115 | sum2 = (uint32_t *) result->second; 116 | 117 | fail_unless(*sum1 == 9, 118 | "Even sum doesn't match (got %"PRIu32 119 | ", expected %"PRIu32")", 120 | *sum1, 9); 121 | 122 | fail_unless(*sum2 == 12, 123 | "Odd sum doesn't match (got %"PRIu32 124 | ", expected %"PRIu32")", 125 | *sum2, 12); 126 | 127 | push_parser_free(parser); 128 | } 129 | END_TEST 130 | 131 | 132 | START_TEST(test_double_sum_02) 133 | { 134 | push_parser_t *parser; 135 | push_callback_t *callback; 136 | push_pair_t *result; 137 | uint32_t *sum1; 138 | uint32_t *sum2; 139 | 140 | PUSH_DEBUG_MSG("---\nStarting test_double_sum_02\n"); 141 | 142 | /* 143 | * If we submit the data twice, we should get twice the result. 144 | */ 145 | 146 | parser = push_parser_new(); 147 | fail_if(parser == NULL, 148 | "Could not allocate a new push parser"); 149 | 150 | callback = make_double_sum_callback(parser); 151 | fail_if(callback == NULL, 152 | "Could not allocate double-sum callback"); 153 | 154 | push_parser_set_callback(parser, callback); 155 | 156 | fail_unless(push_parser_activate(parser, &PAIR_0) 157 | == PUSH_INCOMPLETE, 158 | "Could not activate parser"); 159 | 160 | fail_unless(push_parser_submit_data 161 | (parser, &DATA_01, LENGTH_01) == PUSH_INCOMPLETE, 162 | "Could not parse data"); 163 | 164 | fail_unless(push_parser_submit_data 165 | (parser, &DATA_01, LENGTH_01) == PUSH_INCOMPLETE, 166 | "Could not parse data"); 167 | 168 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 169 | "Shouldn't get parse error at EOF"); 170 | 171 | result = push_parser_result(parser, push_pair_t); 172 | sum1 = (uint32_t *) result->first; 173 | sum2 = (uint32_t *) result->second; 174 | 175 | fail_unless(*sum1 == 18, 176 | "Even sum doesn't match (got %"PRIu32 177 | ", expected %"PRIu32")", 178 | *sum1, 18); 179 | 180 | fail_unless(*sum2 == 24, 181 | "Odd sum doesn't match (got %"PRIu32 182 | ", expected %"PRIu32")", 183 | *sum2, 24); 184 | 185 | push_parser_free(parser); 186 | } 187 | END_TEST 188 | 189 | 190 | START_TEST(test_double_sum_03) 191 | { 192 | push_parser_t *parser; 193 | push_callback_t *callback; 194 | push_pair_t *result; 195 | uint32_t *sum1; 196 | uint32_t *sum2; 197 | 198 | PUSH_DEBUG_MSG("---\nStarting test_double_sum_03\n"); 199 | 200 | /* 201 | * Basically the same as test_double_sum_01, but this one turns 202 | * off the callback's maximum, so it should process all in one go. 203 | * End result should be the same. 204 | */ 205 | 206 | parser = push_parser_new(); 207 | fail_if(parser == NULL, 208 | "Could not allocate a new push parser"); 209 | 210 | callback = make_double_sum_callback(parser); 211 | fail_if(callback == NULL, 212 | "Could not allocate double-sum callback"); 213 | 214 | push_parser_set_callback(parser, callback); 215 | 216 | fail_unless(push_parser_activate(parser, &PAIR_0) 217 | == PUSH_INCOMPLETE, 218 | "Could not activate parser"); 219 | 220 | fail_unless(push_parser_submit_data 221 | (parser, &DATA_01, LENGTH_01) == PUSH_INCOMPLETE, 222 | "Could not parse data"); 223 | 224 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 225 | "Shouldn't get parse error at EOF"); 226 | 227 | result = push_parser_result(parser, push_pair_t); 228 | sum1 = (uint32_t *) result->first; 229 | sum2 = (uint32_t *) result->second; 230 | 231 | fail_unless(*sum1 == 9, 232 | "Even sum doesn't match (got %"PRIu32 233 | ", expected %"PRIu32")", 234 | *sum1, 9); 235 | 236 | fail_unless(*sum2 == 12, 237 | "Odd sum doesn't match (got %"PRIu32 238 | ", expected %"PRIu32")", 239 | *sum2, 12); 240 | 241 | push_parser_free(parser); 242 | } 243 | END_TEST 244 | 245 | 246 | START_TEST(test_misaligned_data) 247 | { 248 | push_parser_t *parser; 249 | push_callback_t *callback; 250 | push_pair_t *result; 251 | uint32_t *sum1; 252 | uint32_t *sum2; 253 | size_t FIRST_CHUNK_SIZE = 7; /* something not divisible by 4 */ 254 | 255 | PUSH_DEBUG_MSG("---\nStarting test_misaligned_data\n"); 256 | 257 | /* 258 | * Our callback processes ints on nice 32-bit boundaries. If we 259 | * send the parser data that doesn't align with these boundaries, 260 | * we should still get the right answer. 261 | */ 262 | 263 | parser = push_parser_new(); 264 | fail_if(parser == NULL, 265 | "Could not allocate a new push parser"); 266 | 267 | callback = make_double_sum_callback(parser); 268 | fail_if(callback == NULL, 269 | "Could not allocate double-sum callback"); 270 | 271 | push_parser_set_callback(parser, callback); 272 | 273 | fail_unless(push_parser_activate(parser, &PAIR_0) 274 | == PUSH_INCOMPLETE, 275 | "Could not activate parser"); 276 | 277 | fail_unless(push_parser_submit_data 278 | (parser, &DATA_01, FIRST_CHUNK_SIZE) == PUSH_INCOMPLETE, 279 | "Could not parse data"); 280 | 281 | fail_unless(push_parser_submit_data 282 | (parser, 283 | ((void *) DATA_01) + FIRST_CHUNK_SIZE, 284 | LENGTH_01 - FIRST_CHUNK_SIZE) == PUSH_INCOMPLETE, 285 | "Could not parse data"); 286 | 287 | fail_unless(push_parser_eof(parser) == PUSH_SUCCESS, 288 | "Shouldn't get parse error at EOF"); 289 | 290 | result = push_parser_result(parser, push_pair_t); 291 | sum1 = (uint32_t *) result->first; 292 | sum2 = (uint32_t *) result->second; 293 | 294 | fail_unless(*sum1 == 9, 295 | "Even sum doesn't match (got %"PRIu32 296 | ", expected %"PRIu32")", 297 | *sum1, 9); 298 | 299 | fail_unless(*sum2 == 12, 300 | "Odd sum doesn't match (got %"PRIu32 301 | ", expected %"PRIu32")", 302 | *sum2, 12); 303 | 304 | push_parser_free(parser); 305 | } 306 | END_TEST 307 | 308 | 309 | /*----------------------------------------------------------------------- 310 | * Testing harness 311 | */ 312 | 313 | Suite * 314 | test_suite() 315 | { 316 | Suite *s = suite_create("double-sum-callback"); 317 | 318 | TCase *tc = tcase_create("double-sum-callback"); 319 | tcase_add_test(tc, test_double_sum_01); 320 | tcase_add_test(tc, test_double_sum_02); 321 | tcase_add_test(tc, test_double_sum_03); 322 | tcase_add_test(tc, test_misaligned_data); 323 | suite_add_tcase(s, tc); 324 | 325 | return s; 326 | } 327 | 328 | 329 | int 330 | main(int argc, const char **argv) 331 | { 332 | int number_failed; 333 | Suite *suite = test_suite(); 334 | SRunner *runner = srunner_create(suite); 335 | 336 | srunner_run_all(runner, CK_NORMAL); 337 | number_failed = srunner_ntests_failed(runner); 338 | srunner_free(runner); 339 | 340 | return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; 341 | } 342 | -------------------------------------------------------------------------------- /src/protobuf/varint64.c: -------------------------------------------------------------------------------- 1 | /* -*- coding: utf-8 -*- 2 | * ---------------------------------------------------------------------- 3 | * Copyright © 2010, RedJack, LLC. 4 | * All rights reserved. 5 | * 6 | * Please see the LICENSE.txt file in this distribution for license 7 | * details. 8 | * ---------------------------------------------------------------------- 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | 22 | /** 23 | * The user data struct for a varint64 callback. 24 | */ 25 | 26 | 27 | typedef struct _varint64 28 | { 29 | /** 30 | * The push_callback_t superclass for this callback. 31 | */ 32 | 33 | push_callback_t callback; 34 | 35 | /** 36 | * The continute continuation that handles the first chunk of data 37 | * we receive. 38 | */ 39 | 40 | push_continue_continuation_t first_cont; 41 | 42 | /** 43 | * The continute continuation that handles the second and later 44 | * chunks of data we receive. 45 | */ 46 | 47 | push_continue_continuation_t rest_cont; 48 | 49 | /** 50 | * The number of bytes currently added to the varint. 51 | */ 52 | 53 | size_t bytes_processed; 54 | 55 | /** 56 | * The parsed value. 57 | */ 58 | 59 | uint64_t value; 60 | 61 | } varint64_t; 62 | 63 | 64 | static void 65 | varint64_rest_continue(void *user_data, 66 | const void *buf, 67 | size_t bytes_remaining) 68 | { 69 | varint64_t *varint64 = (varint64_t *) user_data; 70 | const uint8_t *ibuf = (uint8_t *) buf; 71 | 72 | /* 73 | * If we don't have any data to process, that's a parse error. 74 | */ 75 | 76 | if (bytes_remaining == 0) 77 | { 78 | PUSH_DEBUG_MSG("%s: Reached EOF before end of varint.\n", 79 | push_talloc_get_name(varint64)); 80 | 81 | push_continuation_call(varint64->callback.error, 82 | PUSH_PARSE_ERROR, 83 | "Reached EOF before end of varint"); 84 | 85 | return; 86 | } 87 | 88 | /* 89 | * This continuation gets called for the second and later chunks, 90 | * or during the first chunk if we can't use the fast path. 91 | */ 92 | 93 | uint8_t shift = 7 * varint64->bytes_processed; 94 | 95 | PUSH_DEBUG_MSG("%s: Using slow path on %zu bytes.\n", 96 | push_talloc_get_name(varint64), 97 | bytes_remaining); 98 | 99 | while (bytes_remaining > 0) 100 | { 101 | if (varint64->bytes_processed > PUSH_PROTOBUF_MAX_VARINT_LENGTH) 102 | { 103 | PUSH_DEBUG_MSG("%s: More than %u bytes in value.\n", 104 | push_talloc_get_name(varint64), 105 | PUSH_PROTOBUF_MAX_VARINT_LENGTH); 106 | 107 | push_continuation_call(varint64->callback.error, 108 | PUSH_PARSE_ERROR, 109 | "Varint is too long"); 110 | 111 | return; 112 | } 113 | 114 | PUSH_DEBUG_MSG("%s: Reading byte %zu, shifting by %"PRIu8"\n", 115 | push_talloc_get_name(varint64), 116 | varint64->bytes_processed, shift); 117 | 118 | PUSH_DEBUG_MSG("%s: byte = 0x%02"PRIx8"\n", 119 | push_talloc_get_name(varint64), 120 | *ibuf); 121 | 122 | varint64->value |= ((uint64_t) (*ibuf & 0x7F)) << shift; 123 | shift += 7; 124 | 125 | varint64->bytes_processed++; 126 | buf++; 127 | bytes_remaining--; 128 | 129 | if (*ibuf < 0x80) 130 | { 131 | /* 132 | * This byte ends the varint. 133 | */ 134 | 135 | PUSH_DEBUG_MSG("%s: Read value %"PRIu64 136 | ", using %zu bytes\n", 137 | push_talloc_get_name(varint64), 138 | varint64->value, varint64->bytes_processed); 139 | 140 | push_continuation_call(varint64->callback.success, 141 | &varint64->value, 142 | buf, bytes_remaining); 143 | 144 | return; 145 | } 146 | 147 | ibuf++; 148 | } 149 | 150 | /* 151 | * We ran out of data without processing a full varint, so 152 | * return the incomplete code. 153 | */ 154 | 155 | push_continuation_call(varint64->callback.incomplete, 156 | &varint64->rest_cont); 157 | 158 | return; 159 | } 160 | 161 | 162 | static void 163 | varint64_first_continue(void *user_data, 164 | const void *buf, 165 | size_t bytes_remaining) 166 | { 167 | varint64_t *varint64 = (varint64_t *) user_data; 168 | uint8_t *ibuf = (uint8_t *) buf; 169 | 170 | /* 171 | * If we don't have any data to process, that's a parse error. 172 | */ 173 | 174 | if (bytes_remaining == 0) 175 | { 176 | PUSH_DEBUG_MSG("%s: Reached EOF before end of varint.\n", 177 | push_talloc_get_name(varint64)); 178 | 179 | push_continuation_call(varint64->callback.error, 180 | PUSH_PARSE_ERROR, 181 | "Reached EOF before end of varint"); 182 | 183 | return; 184 | } 185 | 186 | /* 187 | * This continuation gets called for the first data chunk. In 188 | * many cases, we'll be able to use a fast path to parse the value 189 | * super-quickly. 190 | */ 191 | 192 | /* 193 | * If there are enough bytes to read a maximum-length varint, or 194 | * if the last byte in the buffer would end a varint, then we can 195 | * use the “fast path”, and read each byte unchecked. 196 | */ 197 | 198 | if ((bytes_remaining >= PUSH_PROTOBUF_MAX_VARINT_LENGTH) || 199 | (ibuf[bytes_remaining-1] < 0x80)) 200 | { 201 | uint32_t part0 = 0; 202 | uint32_t part1 = 0; 203 | uint32_t part2 = 0; 204 | uint64_t result; 205 | const uint8_t *ptr = (const uint8_t *) buf; 206 | uint8_t b; 207 | 208 | PUSH_DEBUG_MSG("%s: Using fast path\n", 209 | push_talloc_get_name(varint64)); 210 | 211 | /* 212 | * Fast path: we know there are enough bytes in the buffer, so 213 | * we can read each byte unchecked. 214 | */ 215 | 216 | b = *(ptr++); part0 = (b & 0x7F) ; if (!(b & 0x80)) goto done; 217 | b = *(ptr++); part0 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; 218 | b = *(ptr++); part0 |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done; 219 | b = *(ptr++); part0 |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done; 220 | b = *(ptr++); part1 = (b & 0x7F) ; if (!(b & 0x80)) goto done; 221 | b = *(ptr++); part1 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; 222 | b = *(ptr++); part1 |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done; 223 | b = *(ptr++); part1 |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done; 224 | b = *(ptr++); part2 = (b & 0x7F) ; if (!(b & 0x80)) goto done; 225 | b = *(ptr++); part2 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; 226 | 227 | PUSH_DEBUG_MSG("%s: More than %u bytes in value.\n", 228 | push_talloc_get_name(varint64), 229 | PUSH_PROTOBUF_MAX_VARINT_LENGTH); 230 | 231 | push_continuation_call(varint64->callback.error, 232 | PUSH_PARSE_ERROR, 233 | "Varint is too long"); 234 | 235 | return; 236 | 237 | done: 238 | result = 239 | (((uint64_t) part0) ) | 240 | (((uint64_t) part1) << 28) | 241 | (((uint64_t) part2) << 56); 242 | 243 | PUSH_DEBUG_MSG("%s: Read value %"PRIu64", using %zd bytes\n", 244 | push_talloc_get_name(varint64), 245 | result, (ptr - ibuf)); 246 | 247 | buf += (ptr - ibuf); 248 | bytes_remaining -= (ptr - ibuf); 249 | 250 | varint64->value = result; 251 | 252 | push_continuation_call(varint64->callback.success, 253 | &varint64->value, 254 | buf, bytes_remaining); 255 | 256 | return; 257 | } 258 | 259 | /* 260 | * Otherwise, we have to use the slow path. 261 | */ 262 | 263 | return varint64_rest_continue(user_data, buf, bytes_remaining); 264 | } 265 | 266 | 267 | static void 268 | varint64_activate(void *user_data, 269 | void *result, 270 | const void *buf, 271 | size_t bytes_remaining) 272 | { 273 | varint64_t *varint64 = (varint64_t *) user_data; 274 | 275 | PUSH_DEBUG_MSG("%s: Activating with %zu bytes.\n", 276 | push_talloc_get_name(varint64), 277 | bytes_remaining); 278 | 279 | /* 280 | * Initialize the fields that store the current value. 281 | */ 282 | 283 | varint64->bytes_processed = 0; 284 | varint64->value = 0; 285 | 286 | if (bytes_remaining == 0) 287 | { 288 | /* 289 | * If we don't get any data when we're activated, return an 290 | * incomplete and wait for some data. 291 | */ 292 | 293 | push_continuation_call(varint64->callback.incomplete, 294 | &varint64->first_cont); 295 | 296 | return; 297 | 298 | } else { 299 | /* 300 | * Otherwise let the continue continuation go ahead and 301 | * process this chunk of data. 302 | */ 303 | 304 | varint64_first_continue(user_data, buf, bytes_remaining); 305 | return; 306 | } 307 | } 308 | 309 | 310 | push_callback_t * 311 | push_protobuf_varint64_new(const char *name, 312 | void *parent, 313 | push_parser_t *parser) 314 | { 315 | varint64_t *varint64 = push_talloc(parent, varint64_t); 316 | 317 | if (varint64 == NULL) 318 | return NULL; 319 | 320 | /* 321 | * Initialize the push_callback_t instance. 322 | */ 323 | 324 | if (name == NULL) name = "varint64"; 325 | push_talloc_set_name_const(varint64, name); 326 | 327 | push_callback_init(&varint64->callback, parser, varint64, 328 | varint64_activate, 329 | NULL, NULL, NULL); 330 | 331 | /* 332 | * Fill in the continuation objects for the continuations that we 333 | * implement. 334 | */ 335 | 336 | push_continuation_set(&varint64->first_cont, 337 | varint64_first_continue, 338 | varint64); 339 | 340 | push_continuation_set(&varint64->rest_cont, 341 | varint64_rest_continue, 342 | varint64); 343 | 344 | return &varint64->callback; 345 | } 346 | 347 | 348 | /** 349 | * Parse a varint into a size_t. We can only use the varint64 parser 350 | * if size_t is the same size as uint64_t. 351 | */ 352 | 353 | #if SIZE_MAX == UINT64_MAX 354 | 355 | push_callback_t * 356 | push_protobuf_varint_size_new(const char *name, 357 | void *parent, 358 | push_parser_t *parser) 359 | { 360 | return push_protobuf_varint64_new(name, parent, parser); 361 | } 362 | 363 | #endif 364 | --------------------------------------------------------------------------------