| /* |
| * Copyright (C) 2003, 2004, 2005, 2006, 2011, 2014, 2015 Filip Pizlo. All rights reserved. |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * Stuff to know: |
| * |
| * Memory management conventions: unless otherwise stated, all objects |
| * allocated by functions that you call become your responsibility after |
| * a successful return. also, unless otherwise stated, passing an |
| * object as a first argument to a function does not clear your ownership |
| * of that object. and unless otherwise stated, objects passed as anything |
| * but the first argument become the responsibility of that function, |
| * regardless of whether or not that function returns successfully. |
| * |
| * Error management conventions: most functions return pointers or booleans. |
| * in the case of pointers, if the pointer returned is NULL, then you must |
| * check for errors. in the case of booleans, you must check if the |
| * return value is tsf_false. |
| * |
| * Error propagation: those functions that claim to be capable of error |
| * propagation will return immediately as if with an error without actually |
| * setting the error if one of their object arguments is NULL. This allows |
| * you to do: |
| * if (!tsf_do_operation(object1,obj2, ..., tsf_do_op_that_fails(),...)) { |
| * // process error. |
| * } |
| * ... and safely expect that this will correctly catch errors from |
| * tsf_do_op_that_fails() in addition to catching errors from |
| * tsf_do_operation(). |
| * |
| * Code generation/native code support/struct mapping: this functionality is |
| * captured under the 'tsf_native' namespace, but it does not actually have |
| * any struct or class or object of its own. Instead, this functionality's |
| * state is spread across the rest of the system. Fields that have the sole |
| * purpose of supporting code generation or struct mapping are marked as |
| * such with a comment. |
| */ |
| |
| #ifndef FP_TSF_H |
| #define FP_TSF_H |
| |
| #include "tsf_config.h" |
| #include "tsf_atomics.h" |
| #include "tsf_st.h" |
| #include "tsf_region.h" |
| #include "tsf_types.h" |
| #include "tsf_zip_abstract.h" |
| #include "tsf_sha1.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <dirent.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #if 0 |
| /* hack to prevent emacs from being confused */ |
| } |
| #endif |
| |
| /* macro you'll find useful */ |
| #define tsf_offsetof(type,element) ((size_t)(&(((type*)NULL)->element))) |
| |
| /* any errors in this enumeration must also be placed in tsf_error.c's |
| * init_error_array() function. */ |
| enum tsf_error { |
| TSF_E_ERRNO, /* system error; examine errno value returned |
| * from tsf_get_system_errno() */ |
| TSF_E_ZLIB_ERROR, /* ZLib error; examine the error code returned |
| * from tsf_get_zlib_error_code() and also the |
| * error message returned from |
| * tsf_get_zlib_error_msg() */ |
| TSF_E_LIBBZIP2_ERROR, /* libbzip2 error; examine the error code returned |
| * from tsf_get_libbzip2_error_code() and also the |
| * error message returned from |
| * tsf_get_libbzip2_error_msg(). note that the |
| * latter function returns textual messages |
| * selected by TSF and not by libbzip2. these |
| * messages were written by me based on the |
| * libbzip2 manual. */ |
| TSF_E_INVALID_ARG, /* invalid argument */ |
| TSF_E_BAD_STATE, /* bad state - the given operation doesn't make |
| * sense for the current state of the object. */ |
| TSF_E_STR_OVERFLOW, /* string overflow. strings used as element names |
| * in structs and choices cannot be longer than |
| * TSF_STR_SIZE (including null byte). comment |
| * strings also have some limit. */ |
| TSF_E_PARSE_ERROR, /* parse error */ |
| TSF_E_UNEXPECTED_TYPE, /* did not parse the type that I expected to |
| * parse. this may happen if you're asking the |
| * serial in man to re-read some data that it's |
| * already read. you can do this by using its |
| * mark and seek mechanism. */ |
| TSF_E_ELEMENT_NULL, /* element in container is null */ |
| TSF_E_NAME_COLLISION, /* name collision - the name is already used! */ |
| TSF_E_EOF, /* end of file or end of stream */ |
| TSF_E_UNEXPECTED_EOF, /* unexpected end of file or end of stream. when |
| * returned from a reader, this error means that |
| * the EOF happened before any data was read. */ |
| TSF_E_ERRONEOUS_EOF, /* erroneous end of file or end of stream. when |
| * returned from a reader, this error means that |
| * the EOF happened after some data was already |
| * read. to everyone else, this error should |
| * have the same exact meaning as |
| * TSF_E_UNEXPECTED_EOF */ |
| TSF_E_NO_EOF, /* expected an EOF but didn't get one. */ |
| TSF_E_BAD_TYPE, /* parameter has the wrong tsf_type - thrown |
| * when the type for a typed object (such as |
| * a reflect object) is not what is expected. */ |
| TSF_E_BAD_TYPE_KIND, /* parameter has the wrong tsf_type_kind - |
| * thrown when you pass the wrong type object |
| * into a function. */ |
| TSF_E_ELE_NOT_FOUND, /* element not found */ |
| TSF_E_NO_STRUCT_MAP, /* the given type lacks a struct mapping */ |
| TSF_E_BAD_STRUCT_MAP, /* the structure mapping offset or size is |
| * invalid because it does not agree with the |
| * offsets and/or sizes of the other elements. |
| * note that this is currently unused, but we |
| * keep it here to reserve space. */ |
| TSF_E_TYPE_MISMATCH, /* type mismatch */ |
| TSF_E_BAD_CHOICE_VALUE, /* bad choice value. note that if a bad choice |
| * value appears while parsing, you will get a |
| * TSF_E_PARSE_ERROR instead. also, there will |
| * be times when you don't get this error even |
| * though you should have. so be happy when |
| * you see it and don't cry to me about it if you |
| * don't. */ |
| TSF_E_COMPARE_FAIL, /* a comparison failed. */ |
| TSF_E_COMPILE_ERROR, /* compilation error in dyncc */ |
| TSF_E_DEATH_BY_SIGNAL, /* child process died because it received a |
| * signal (again, this'll happen because of |
| * dyncc). */ |
| TSF_E_DLOADING_ERROR, /* dynamic loading error in dyncc */ |
| TSF_E_BAD_JUMP, /* jump to bad label in GPC prototype */ |
| TSF_E_SEMANTIC, /* violation of GPC semantics */ |
| TSF_E_CAS_FAIL, /* a compare-and-swap-like operation failed. */ |
| TSF_E_HOSTNAME, /* hostname lookup failure. */ |
| TSF_E_NOT_ALLOWED, /* you get this error if you had specified that |
| * a certain action was not allowed, but for |
| * whatever reason TSF would have had to perform |
| * that action. */ |
| TSF_E_NOT_IMPLEMENTED, /* not implemented. this differs from not |
| * supported in that something that is 'not |
| * implemented' is actually advertised as being |
| * supported (and implemented), but I just |
| * haven't moved my ass to implement it. you |
| * should only see 'not implemented' errors in |
| * development versions. */ |
| TSF_E_NOT_SUPPORTED, /* not supported. this differs from not |
| * implemented in that 'not supported' refers |
| * to functionality that is present but cannot |
| * be used due to some limitations of your |
| * system. */ |
| TSF_E_EXTERNAL, /* some external error (like a condition in |
| * in a callback provided by someone else). |
| * the C++ binding uses this for |
| * std::exception objects caught in callbacks. */ |
| TSF_E_INTERNAL, /* internal error - should never happen. */ |
| TSF_E_LAST /* placeholder */ |
| }; |
| |
| typedef enum tsf_error tsf_error_t; |
| |
| /* standard string size within tsf */ |
| #define TSF_STR_SIZE 128 |
| |
| /* max size for comment strings */ |
| #define TSF_MAX_COMMENT_SIZE 4096 |
| |
| /* max size for type name */ |
| #define TSF_MAX_NAME_SIZE 256 |
| |
| /* default buffer size */ |
| #define TSF_DEF_BUF_SIZE 4096 |
| |
| /* default buffer size for network connections */ |
| #define TSF_DEF_NET_BUF_SIZE (1460*32) |
| |
| /* Instanceof rules: |
| * -> VOID type. everything is instanceof VOID, but VOID is instaceof |
| * VOID only. |
| * -> INT types. An INT type is instanceof any INT type that is |
| * composed of at least as many bits as it. For example, INT8 |
| * is instanceof INT8, INT16, INT32, and INT64. INT types are |
| * not instanceof any UINT types. INT types are instanceof |
| * those FP types that have a mantissa that is made up of at |
| * least as many bits as it. |
| * -> UINT types. A UINT type is instanceof any UINT type that is |
| * composed of at least as many bits as it. Also, a UINT type |
| * is instanceof any INT type that has at least one more bits |
| * than it (so UINT16 will be instanceof INT32 and INT64, but |
| * not INT16). UINT types are instanceof those FP types that |
| * have a mantissa that is made up of at least one more bits as |
| * it. |
| * -> INTEGER. This behaves like a 32-bit signed integer, but it |
| * is stored with some basic compaction under the assumption |
| * that it is usually smallish. |
| * -> LONG. Same as INTEGER, but gives you 64 bits. |
| * -> FP types. An FP type is instanceof any FP type that has at |
| * least as large of a mantissa, and at least as large of an |
| * exponent. So FLOAT is instanceof FLOAT and DOUBLE, while |
| * DOUBLE is only instanceof DOUBLE. |
| * -> ARRAY. if A and B are array types, then A is instanceof B |
| * if element_type(A) is instanceof element_type(B). |
| * -> STRUCT. one struct is instanceof another if it contains all |
| * of the mandatory elements of the other struct, and if each |
| * such element is instanceof its respective element in the other |
| * struct. note that if a struct contains an optional field for |
| * another struct's mandatory field, this will violate the |
| * instanceof relation. |
| * -> BIT. a BIT is only instanceof BIT. the justification is that |
| * despite the name, which would seem to imply that a BIT is a |
| * type of integer, a BIT is intended to be used in such a |
| * fundamentally different way that it would not make sense for |
| * a BIT to be instanceof any integer. |
| * -> CHOICE. if A and B are choice types, then A is instanceof B |
| * if for those element names that A and B have in common, A's |
| * element is instanceof B's element. |
| * -> STRING. a STRING is only instanceof STRING. |
| * -> ANY. a ANY is only instanceof ANY. this is of |
| * course quite ironic, since ANY types are our main source of |
| * polymorhpism. :-) |
| * |
| * Note that if you change this enumeration, you must change some of the |
| * #define's further down in this file. |
| */ |
| enum tsf_type_kind { |
| TSF_TK_VOID = 0, |
| |
| /* ints and floats are primitives. bits are not. */ |
| |
| TSF_TK_INT8 = 1, |
| TSF_TK_UINT8 = 2, |
| TSF_TK_INT16 = 3, |
| TSF_TK_UINT16 = 4, |
| TSF_TK_INT32 = 5, |
| TSF_TK_UINT32 = 6, |
| TSF_TK_INT64 = 7, |
| TSF_TK_UINT64 = 8, |
| /* reserve 9-13 for bizarre integer types. */ |
| |
| /* This is a 32-bit integer (TSF_TK_INTEGER) or 64-bit integer |
| * (TSF_TK_LONG) that we compact down to 1 byte if possible whenever we |
| * transmit it. The rules for integer compaction given an integer X are: |
| * |
| * - If -64 <= X < 64, encode as one byte containing X with the high bit |
| * of that byte set to 0. Else: |
| * - Let X' = (X < 0) ? X + 64 : X - 64. If -16192 <= X < 16192, encode |
| * as one byte containing (X' >> 8) - 1 with the high bit of that byte |
| * set to 1 followed by a second byte containing X' & 255. Else: |
| * - Let X' = (X < 0) ? X + 16192 : X - 16192. If -8404800 <= X < 8404800, |
| * encode as one byte containing 254 followed by three bytes containing |
| * X'. Else: |
| * - Encode as 255 followed by either four bytes (TSF_TK_INTEGER) or |
| * eight bytes (TSF_TK_LONG) containin X. |
| * |
| * Note that as with everything else in TSF, the protocol is defined as |
| * big endian. */ |
| TSF_TK_INTEGER = 14, |
| TSF_TK_LONG = 15, |
| /* reserve 16-19 for more bizarre integer types. */ |
| |
| TSF_TK_FLOAT = 20, |
| TSF_TK_DOUBLE = 21, |
| /* reserve 22-39 for bizarre float types. recommend that if |
| * we ever have a customizable float, it should be 30. */ |
| |
| TSF_TK_BIT = 40, |
| |
| TSF_TK_STRING = 45, |
| |
| TSF_TK_ARRAY = 50, |
| TSF_TK_STRUCT = 51, |
| TSF_TK_CHOICE = 52, |
| |
| TSF_TK_ANY = 60, |
| }; |
| |
| typedef uint8_t tsf_type_kind_t; |
| |
| struct tsf_type; |
| typedef struct tsf_type tsf_type_t; |
| |
| struct tsf_named_type; |
| typedef struct tsf_named_type tsf_named_type_t; |
| |
| /* ordered table of types keyed by int and string */ |
| struct tsf_type_table; |
| typedef struct tsf_type_table tsf_type_table_t; |
| |
| /* shared table of types keyed only by int */ |
| struct tsf_type_in_map; |
| typedef struct tsf_type_in_map tsf_type_in_map_t; |
| |
| typedef tsf_st_table tsf_type_out_map_t; |
| |
| struct tsf_ra_type_info; |
| struct tsf_ra_tc_node; |
| struct tsf_ra_type_man; |
| |
| typedef struct tsf_ra_type_info tsf_ra_type_info_t; |
| typedef struct tsf_ra_tc_node tsf_ra_tc_node_t; |
| typedef struct tsf_ra_type_man tsf_ra_type_man_t; |
| |
| struct tsf_buffer; |
| typedef struct tsf_buffer tsf_buffer_t; |
| |
| /* type used for arrays referenced from structs within our native |
| * structure mapping. you do not have to ever touch this type. but |
| * if you do struct mapping (which surely you will!), then your |
| * arrays should resemble this here tsf_native_array. */ |
| struct tsf_native_array { |
| void *data; |
| uint32_t len; |
| }; |
| |
| typedef struct tsf_native_array tsf_native_array_t; |
| |
| /* types used internally for primitive arrays */ |
| #define TSF_DEFINE_NATIVE_ARRAY(type,suffix) \ |
| struct tsf_native_##type##_array {\ |
| type##suffix *data;\ |
| uint32_t len;\ |
| };\ |
| \ |
| typedef struct tsf_native_##type##_array tsf_native_##type##_array_t |
| |
| TSF_DEFINE_NATIVE_ARRAY(int8,_t); |
| TSF_DEFINE_NATIVE_ARRAY(int16,_t); |
| TSF_DEFINE_NATIVE_ARRAY(int32,_t); |
| TSF_DEFINE_NATIVE_ARRAY(int64,_t); |
| TSF_DEFINE_NATIVE_ARRAY(uint8,_t); |
| TSF_DEFINE_NATIVE_ARRAY(uint16,_t); |
| TSF_DEFINE_NATIVE_ARRAY(uint32,_t); |
| TSF_DEFINE_NATIVE_ARRAY(uint64,_t); |
| TSF_DEFINE_NATIVE_ARRAY(float,); |
| TSF_DEFINE_NATIVE_ARRAY(double,); |
| |
| /* special type used for arrays of unknown type */ |
| struct tsf_native_void_array { |
| uint32_t len; |
| }; |
| |
| typedef struct tsf_native_void_array tsf_native_void_array_t; |
| |
| /* special type used for arrays of bits referenced from structs |
| * within our native structure mapping. */ |
| struct tsf_native_bitvector { |
| uint8_t *bits; /* array of length (num_bits+7)>>3 */ |
| uint32_t num_bits; |
| }; |
| |
| typedef struct tsf_native_bitvector tsf_native_bitvector_t; |
| |
| /* type of the choice itself within choice types. */ |
| typedef uint32_t tsf_choice_t; |
| |
| struct tsf_reflect; |
| typedef struct tsf_reflect tsf_reflect_t; |
| |
| struct tsf_genrtr; |
| struct tsf_parser; |
| struct tsf_copier; |
| struct tsf_destructor; |
| typedef struct tsf_genrtr tsf_genrtr_t; |
| typedef struct tsf_parser tsf_parser_t; |
| typedef struct tsf_copier tsf_copier_t; |
| typedef struct tsf_destructor tsf_destructor_t; |
| |
| /* function callback used for reading. return tsf_true on success and |
| * tsf_false on error. on error, use tsf_set_error() and tsf_set_errno() |
| * to indicate what error occurred (this part is not optional - you |
| * cannot return tsf_false without first calling tsf_set_error() or |
| * tsf_set_errno()). an all-or-nothing read is expected, so if you're |
| * implementing this as a read on a socket or pipe, you may have to |
| * loop a couple of times. */ |
| typedef tsf_bool_t (*tsf_reader_t)(void *arg, |
| void *buf, |
| uint32_t len); |
| |
| /* function callback used for write. return tsf_true on success and |
| * tsf_false on error. on error, use tsf_set_error() and tsf_set_errno() |
| * to indicate what error occurred (this part is not optional - you |
| * cannot return tsf_false without first calling tsf_set_error() or |
| * tsf_set_errno()). an all-or-nothing write is expected, so if you're |
| * implementing this as a write on a socket or pipe, you may have to |
| * loop a couple of times. */ |
| typedef tsf_bool_t (*tsf_writer_t)(void *arg, |
| const void *buf, |
| uint32_t len); |
| |
| /* function callback used for reading. returns non-negative count of |
| * the number of bytes read on success and -1 on error. on error, use |
| * tsf_set_error() and friends. the caller expects read-as-much-as- |
| * you-can semantics, so this function should never block if some data |
| * is already available. */ |
| typedef int32_t (*tsf_partial_reader_t)(void *arg, |
| void *buf, |
| uint32_t len); |
| |
| struct tsf_limits { |
| /* maximum number of types accepted in a stream file in between reset |
| * types calls. */ |
| uint32_t max_types; |
| |
| /* byte size limits */ |
| |
| /* maximum size in bytes of a serialized type */ |
| uint32_t max_type_size; |
| |
| /* maximum size that all types read during a session can have put |
| * together. */ |
| uint32_t max_total_type_size; |
| |
| /* maximum size in bytes of a buffer */ |
| uint32_t max_buf_size; |
| |
| /* container depth limit */ |
| uint32_t depth_limit; |
| |
| /* whether or not to allow comments */ |
| tsf_bool_t allow_comments; |
| |
| /* allowing individual types. access this as a regular bitvector. */ |
| uint8_t allow_type_kind[32]; |
| |
| /* type-specific limits */ |
| |
| /* maximum array length */ |
| uint32_t max_array_length; /* unimplemented */ |
| |
| /* maximum struct size, in number of elements */ |
| uint32_t max_struct_size; |
| |
| /* maximum choice size, in number of elements */ |
| uint32_t max_choice_size; |
| |
| /* maximum string size */ |
| uint32_t max_string_size; /* unimplemented */ |
| }; |
| |
| typedef struct tsf_limits tsf_limits_t; |
| |
| struct tsf_serial_in_man; |
| struct tsf_serial_out_man; |
| |
| typedef struct tsf_serial_in_man tsf_serial_in_man_t; |
| typedef struct tsf_serial_out_man tsf_serial_out_man_t; |
| |
| /* callback that gets called by the input manager when a new type comes |
| * in. */ |
| typedef void (*tsf_type_cback_t)(tsf_serial_in_man_t *in_man, |
| tsf_type_t *type, |
| void *arg); |
| |
| typedef uint32_t tsf_serial_in_man_state_t; |
| |
| /* size aware - delegates to another reader, counts the bytes read, |
| * and fires a TSF_E_NOT_ALLOWED error when the limit reaches 0. name |
| * should be a short (preferrably one-word) description of what you're |
| * reading; it'll be used for the error message. */ |
| typedef struct { |
| tsf_reader_t reader; |
| void *arg; |
| uint32_t limit; |
| const char *name; |
| } tsf_size_aware_rdr_t; |
| |
| /* light-weight representation of a buffer for writing. */ |
| typedef struct { |
| uint8_t *base; |
| uint8_t *cur; |
| uint8_t *end; |
| } tsf_resizable_buffer_t; |
| |
| /* light-weight representation of a buffer */ |
| typedef struct { |
| uint8_t *cur, |
| *end; |
| } tsf_light_buffer_t; |
| |
| typedef struct { |
| tsf_reader_t reader; |
| void *arg; |
| } tsf_comparing_writer_t; |
| |
| struct tsf_sha1_wtr; |
| typedef struct tsf_sha1_wtr tsf_sha1_wtr_t; |
| |
| struct tsf_buf_rdr; |
| struct tsf_buf_wtr; |
| typedef struct tsf_buf_rdr tsf_buf_rdr_t; |
| typedef struct tsf_buf_wtr tsf_buf_wtr_t; |
| |
| /* the zlib rdr/wtr can also do bzip2. */ |
| |
| struct tsf_zip_wtr_attr { |
| tsf_zip_mode_t mode; |
| |
| uint32_t buf_size; |
| |
| union { |
| struct { |
| tsf_bool_t advanced_init; |
| |
| int level; |
| |
| int method; |
| int windowBits; |
| int memLevel; |
| int strategy; |
| |
| int reserved1; |
| int reserved2; |
| int reserved3; |
| } z; |
| struct { |
| int blockSize100k; |
| int verbosity; |
| int workFactor; |
| |
| int reserved1; |
| int reserved2; |
| int reserved3; |
| } b; |
| } u; |
| }; |
| |
| struct tsf_zip_rdr_attr { |
| struct { |
| tsf_bool_t allow; |
| |
| uint32_t buf_size; |
| tsf_bool_t advanced_init; |
| int windowBits; |
| |
| int reserved1; |
| int reserved2; |
| int reserved3; |
| } z; |
| struct { |
| tsf_bool_t allow; |
| |
| uint32_t buf_size; |
| int verbosity; |
| int small; |
| |
| int reserved1; |
| int reserved2; |
| int reserved3; |
| } b; |
| }; |
| |
| typedef struct tsf_zip_wtr_attr tsf_zip_wtr_attr_t; |
| typedef struct tsf_zip_rdr_attr tsf_zip_rdr_attr_t; |
| |
| struct tsf_zip_rdr; |
| struct tsf_zip_wtr; |
| typedef struct tsf_zip_rdr tsf_zip_rdr_t; |
| typedef struct tsf_zip_wtr tsf_zip_wtr_t; |
| |
| struct tsf_adpt_rdr; |
| typedef struct tsf_adpt_rdr tsf_adpt_rdr_t; |
| |
| /* you're responsible for allocating these guys yourself. but that does |
| * not mean that you can edit them! treat this as an opaque! also note |
| * that the state is effectively a pointer, so this mark is undefined if |
| * serialized... so you cannot use these things to build an external |
| * index. sorry. */ |
| struct tsf_stream_file_mark { |
| tsf_off_t offset; |
| tsf_serial_in_man_state_t state; |
| }; |
| |
| typedef struct tsf_stream_file_mark tsf_stream_file_mark_t; |
| |
| struct tsf_stream_file_input; |
| struct tsf_stream_file_output; |
| typedef struct tsf_stream_file_input tsf_stream_file_input_t; |
| typedef struct tsf_stream_file_output tsf_stream_file_output_t; |
| |
| enum tsf_fsdb_kind { |
| TSF_FSDB_LOCAL, |
| TSF_FSDB_REMOTE |
| }; |
| |
| typedef enum tsf_fsdb_kind tsf_fsdb_kind_t; |
| |
| struct tsf_fsdk_connection; |
| typedef struct tsf_fsdb_connection tsf_fsdb_connection_t; |
| |
| struct tsf_fsdb; |
| typedef struct tsf_fsdb tsf_fsdb_t; |
| |
| struct tsf_fsdb_in; |
| struct tsf_fsdb_out; |
| typedef struct tsf_fsdb_in tsf_fsdb_in_t; |
| typedef struct tsf_fsdb_out tsf_fsdb_out_t; |
| |
| /* get an error code. this is the error code from the last function that |
| * failed in the current thread. functions in the TSF API always tell you |
| * whether or not they failed, usually by returning NULL (if the function |
| * returns pointers), tsf_false (if the function returns tsf_bool_t), or -1 |
| * (if the function returns some sort of integer). calls to this function |
| * made when no error has occurred result in undefined behavior (read: |
| * segfault). */ |
| tsf_error_t tsf_get_error_code(void); |
| |
| /* given an error code, tells you what the default message is. the string |
| * returned here is truly a constant, so you can safely refer to it |
| * indefinitely. note that if you want a more descriptive message for an |
| * error that just occurred, use tsf_get_error(). tsf_get_error() will |
| * almost always return a string that contains more information than using |
| * tsf_get_message_for_error_code(tsf_get_error_code()). note that this |
| * function does not check if code is a valid TSF error code; if it isn't, |
| * bad things will happen. */ |
| const char* tsf_get_message_for_error_code(tsf_error_t code); |
| |
| /* get the system errno associated with the current error. the return |
| * value is undefined if the last function that failed did not fail due to |
| * a system error. */ |
| int tsf_get_system_errno(void); |
| |
| /* get the ZLib error code associated with the current error. the return |
| * value is undefined if the last function that failed did not fail due to |
| * a ZLib error. */ |
| int tsf_get_zlib_error_code(void); |
| |
| /* get the ZLib error message associated with the current error. the return |
| * value is undefined if the last function that failed did not fail due to |
| * a ZLib error. (NOTE: if a ZLib error actually did happen, this will |
| * not be NULL.)*/ |
| const char* tsf_get_zlib_error_msg(void); |
| |
| /* get the libbzip2 error code associated with the current error. the return |
| * value is undefined if the last function that failed did not fail due to |
| * a libbzip2 error. */ |
| int tsf_get_libbzip2_error_code(void); |
| |
| /* get the libbzip2 error message associated with the current error. the |
| * return value is undefined if the last function that failed did not fail |
| * due to a libbzip2 error.*/ |
| const char* tsf_get_libbzip2_error_msg(void); |
| |
| /* get the signal that killed the child process (only for |
| * TSF_E_DEATH_BY_SIGNAL). the return value is undefined if the last |
| * function that failed did not fail due to TSF_E_DEATH_BY_SIGNAL. */ |
| int tsf_get_deadly_signal(void); |
| |
| /* get a description of the error. the string that is returned |
| * is not owned by you; it is only valid until the next call to |
| * any tsf function other than tsf_get_error_code(), tsf_get_error(), |
| * and the other error-getting functions above. (If the library is |
| * compiled with thread-safety then the previous statement refers to |
| * the next call by the same thread, rather than the next call |
| * globally.) calls to this function made when no error has |
| * occurred (within this thread) result in undefined behavior |
| * (quite possibly and very likely a segfault inside tsf_get_error()). */ |
| const char* tsf_get_error(void); |
| |
| /* get just the message for the current error code. this will always |
| * be the prefix of what tsf_get_error() returns. */ |
| #define tsf_get_error_prefix() \ |
| (tsf_get_message_for_error_code(tsf_get_error_code())) |
| |
| /* get just the message that is acquired from the source of the error. |
| * for errno, this is strerror(errno); , and so on. this will be NULL for |
| * errors in which this does not apply. if the error is set using the |
| * ordinary functions in this header file, this string will fall somewhere |
| * in the string returned by tsf_get_error() - but to be safe you should |
| * not rely on this fact. */ |
| const char* tsf_get_error_infix(void); |
| |
| /* get just the message that was supplied by the user. this will be |
| * NULL if the user did not supply anything. if non-null, this will |
| * always be the suffix of what tsf_get_error() returns. */ |
| const char* tsf_get_error_suffix(void); |
| |
| /* specify that an error occurred. format may be NULL, in which case |
| * the only error string is the one that comes with the given error |
| * code. */ |
| void tsf_set_error(tsf_error_t code, |
| const char *format, |
| ...); |
| |
| /* specify that a system error ocurred. if format is NULL, then the |
| * error message contains the string "System error" and the strerror |
| * for the given errno. if format is non-NULL, then your text |
| * gets appended at the end of that. */ |
| void tsf_set_specific_errno(int cur_errno, |
| const char *format,...); |
| |
| /* specify that a system error ocurred. exactly like |
| * tsf_set_specific_errno() except that the global errno is used |
| * instead of the given errno. */ |
| void tsf_set_errno(const char *format,...); |
| |
| /* specify that some child process died with the given signal. */ |
| void tsf_set_deadly_signal(int deadly_signal, |
| const char *format,...); |
| |
| /* specify that a ZLib error occurred. if format is NULL, then the |
| * error message contains the string "ZLib error" and the error message |
| * you provide. If the format is not NULL, the text specified by the |
| * format is appended to that. |
| * |
| * Note that if ZLib support is not compiled in, this function will |
| * mean an immediate abort. */ |
| void tsf_set_zlib_error(int error_code, |
| const char *error_msg, |
| const char *format, |
| ...); |
| |
| /* specify that a libbzip2 error occurred. if format is NULL, then the |
| * error message contains the string "libbzip2 error" and an error message |
| * that is appropriate to the error code. If the format is not NULL, the |
| * text specified by the format is appended to that. |
| * |
| * Note that if libbzip2 support is not compiled in, this function will |
| * mean an immediate abort. */ |
| void tsf_set_libbzip2_error(int error_code, |
| const char *format, |
| ...); |
| |
| /* returns the library's version string. */ |
| const char* tsf_version(void); |
| |
| /* returns the library's major version number. */ |
| int tsf_version_major(void); |
| |
| /* returns the library's minor version number. */ |
| int tsf_version_minor(void); |
| |
| /* returns the library's patch version number. */ |
| int tsf_version_patch(void); |
| |
| /* creates default limits */ |
| tsf_limits_t* tsf_limits_create(void); |
| |
| /* destroys limits, but only if they're not NULL */ |
| void tsf_limits_destroy(tsf_limits_t *limits); |
| |
| /* clones limits. returns NULL if the type it's given is NULL. this |
| * counts as error propagation. */ |
| tsf_limits_t* tsf_limits_clone(tsf_limits_t *limits); |
| |
| /* set the allow bit of the particular kind type */ |
| static TSF_inline |
| void tsf_limits_set_allow_type_kind(tsf_limits_t *limits, |
| tsf_type_kind_t kind_code) { |
| limits->allow_type_kind[kind_code>>3]|=(1<<(kind_code&7)); |
| } |
| |
| /* reset the allow bit of the particular kind type */ |
| static TSF_inline |
| void tsf_limits_reset_allow_type_kind(tsf_limits_t *limits, |
| tsf_type_kind_t kind_code) { |
| limits->allow_type_kind[kind_code>>3]&=~(1<<(kind_code&7)); |
| } |
| |
| /* determine if the allow bit of the particular kind type is set. always |
| * returns tsf_true if limits is NULL. */ |
| static TSF_inline |
| tsf_bool_t tsf_limits_is_type_kind_allowed(tsf_limits_t *limits, |
| tsf_type_kind_t kind_code) { |
| if (limits==NULL) { |
| return tsf_true; |
| } |
| return (limits->allow_type_kind[kind_code>>3]&(1<<(kind_code&7))) |
| ?tsf_true:tsf_false; |
| } |
| |
| /* given a kind code, gives you the textual tsf name for it. */ |
| static TSF_inline |
| const char* tsf_type_kind_tsf_name(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: return "TSF_TK_INT8"; |
| case TSF_TK_UINT8: return "TSF_TK_UINT8"; |
| case TSF_TK_INT16: return "TSF_TK_INT16"; |
| case TSF_TK_UINT16: return "TSF_TK_UINT16"; |
| case TSF_TK_INT32: return "TSF_TK_INT32"; |
| case TSF_TK_UINT32: return "TSF_TK_UINT32"; |
| case TSF_TK_INT64: return "TSF_TK_INT64"; |
| case TSF_TK_UINT64: return "TSF_TK_UINT64"; |
| case TSF_TK_INTEGER: return "TSF_TK_INTEGER"; |
| case TSF_TK_LONG: return "TSF_TK_LONG"; |
| case TSF_TK_FLOAT: return "TSF_TK_FLOAT"; |
| case TSF_TK_DOUBLE: return "TSF_TK_DOUBLE"; |
| case TSF_TK_ARRAY: return "TSF_TK_ARRAY"; |
| case TSF_TK_STRUCT: return "TSF_TK_STRUCT"; |
| case TSF_TK_BIT: return "TSF_TK_BIT"; |
| case TSF_TK_CHOICE: return "TSF_TK_CHOICE"; |
| case TSF_TK_VOID: return "TSF_TK_VOID"; |
| case TSF_TK_STRING: return "TSF_TK_STRING"; |
| case TSF_TK_ANY: return "TSF_TK_ANY"; |
| default: return "unknown"; |
| } |
| } |
| |
| /* tells you if a kind code represents a primitive type */ |
| static TSF_inline |
| tsf_bool_t tsf_type_kind_is_primitive(tsf_type_kind_t kind_code) { |
| return kind_code>=TSF_TK_INT8 && kind_code<=TSF_TK_DOUBLE; |
| } |
| |
| /* given a primitive kind code, it returns to you the size in bytes. this |
| * is the native size of the type, and for everything but TSF_TK_INTEGER |
| * and TSF_TK_LONG, it's also the encoded size. */ |
| static TSF_inline |
| uint32_t tsf_primitive_type_kind_size_of(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: return 1; |
| case TSF_TK_UINT8: return 1; |
| case TSF_TK_INT16: return 2; |
| case TSF_TK_UINT16: return 2; |
| case TSF_TK_INT32: return 4; |
| case TSF_TK_UINT32: return 4; |
| case TSF_TK_INTEGER: return 4; |
| case TSF_TK_INT64: return 8; |
| case TSF_TK_UINT64: return 8; |
| case TSF_TK_LONG: return 8; |
| case TSF_TK_FLOAT: return 4; |
| case TSF_TK_DOUBLE: return 8; |
| default: return 0; |
| } |
| } |
| |
| /* returns a range lower bound ID */ |
| static TSF_inline |
| int32_t tsf_primitive_type_kind_lower_bound(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: return -1; |
| case TSF_TK_UINT8: return 0; |
| case TSF_TK_INT16: return -3; |
| case TSF_TK_UINT16: return 0; |
| case TSF_TK_INT32: return -5; |
| case TSF_TK_INTEGER: return -5; |
| case TSF_TK_UINT32: return 0; |
| case TSF_TK_INT64: return -7; |
| case TSF_TK_UINT64: return 0; |
| case TSF_TK_LONG: return -7; |
| case TSF_TK_FLOAT: return -9; |
| case TSF_TK_DOUBLE: return -10; |
| default: return 0; |
| } |
| } |
| |
| /* returns a range upper bound ID */ |
| static TSF_inline |
| int32_t tsf_primitive_type_kind_upper_bound(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: return 1; |
| case TSF_TK_UINT8: return 2; |
| case TSF_TK_INT16: return 3; |
| case TSF_TK_UINT16: return 4; |
| case TSF_TK_INT32: return 5; |
| case TSF_TK_INTEGER: return 5; |
| case TSF_TK_UINT32: return 6; |
| case TSF_TK_INT64: return 7; |
| case TSF_TK_UINT64: return 8; |
| case TSF_TK_LONG: return 8; |
| case TSF_TK_FLOAT: return 9; |
| case TSF_TK_DOUBLE: return 10; |
| default: return 0; |
| } |
| } |
| |
| /* returns the precision of a primitive type */ |
| static TSF_inline |
| int32_t tsf_primitive_type_kind_precision(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: return 8; |
| case TSF_TK_UINT8: return 8; |
| case TSF_TK_INT16: return 16; |
| case TSF_TK_UINT16: return 16; |
| case TSF_TK_INT32: return 32; |
| case TSF_TK_UINT32: return 32; |
| case TSF_TK_INTEGER: return 32; |
| case TSF_TK_INT64: return 64; |
| case TSF_TK_UINT64: return 64; |
| case TSF_TK_LONG: return 64; |
| case TSF_TK_FLOAT: return 23; |
| case TSF_TK_DOUBLE: return 52; |
| default: return 0; |
| } |
| } |
| |
| /* given a primitive kind code, tells you if it is floating point. */ |
| static TSF_inline |
| tsf_bool_t tsf_primitive_type_kind_is_float(tsf_type_kind_t kind_code) { |
| return kind_code==TSF_TK_FLOAT || kind_code==TSF_TK_DOUBLE; |
| } |
| |
| /* same as above. */ |
| #define tsf_type_kind_is_float(kind_code) \ |
| (tsf_primitive_type_kind_is_float(kind_code)) |
| |
| /* given a primitive kind code, tells you if it is floating point. */ |
| static TSF_inline |
| tsf_bool_t tsf_primitive_type_kind_is_int(tsf_type_kind_t kind_code) { |
| return tsf_type_kind_is_primitive(kind_code) && |
| !tsf_type_kind_is_float(kind_code); |
| } |
| |
| /* same as above. */ |
| #define tsf_type_kind_is_int(kind_code) \ |
| (tsf_primitive_type_kind_is_int(kind_code)) |
| |
| /* given a primitive kind code, tells you if it is signed. */ |
| static TSF_inline |
| tsf_bool_t tsf_primitive_type_kind_is_signed(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: |
| case TSF_TK_INT16: |
| case TSF_TK_INT32: |
| case TSF_TK_INT64: |
| case TSF_TK_INTEGER: |
| case TSF_TK_LONG: return tsf_true; |
| default: return tsf_primitive_type_kind_is_float(kind_code); |
| } |
| } |
| |
| /* same as above. */ |
| #define tsf_type_kind_is_signed(kind_code) \ |
| (tsf_primitive_type_kind_is_signed(kind_code)) |
| |
| /* given a primitive kind code, gives you the textual name of the |
| * C type. */ |
| static TSF_inline |
| const char* tsf_primitive_type_kind_c_name(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: return "int8_t"; |
| case TSF_TK_UINT8: return "uint8_t"; |
| case TSF_TK_INT16: return "int16_t"; |
| case TSF_TK_UINT16: return "uint16_t"; |
| case TSF_TK_INT32: return "int32_t"; |
| case TSF_TK_UINT32: return "uint32_t"; |
| case TSF_TK_INT64: return "int64_t"; |
| case TSF_TK_UINT64: return "uint64_t"; |
| case TSF_TK_INTEGER: return "tsf_integer_t"; |
| case TSF_TK_LONG: return "tsf_long_t"; |
| case TSF_TK_FLOAT: return "float"; |
| case TSF_TK_DOUBLE: return "double"; |
| default: return "unknown"; |
| } |
| } |
| |
| /* given a primitive kind code, gives you the textual name of the type |
| * using lower case. */ |
| static TSF_inline |
| const char* tsf_primitive_type_kind_lc_name(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: return "int8"; |
| case TSF_TK_UINT8: return "uint8"; |
| case TSF_TK_INT16: return "int16"; |
| case TSF_TK_UINT16: return "uint16"; |
| case TSF_TK_INT32: return "int32"; |
| case TSF_TK_UINT32: return "uint32"; |
| case TSF_TK_INT64: return "int64"; |
| case TSF_TK_UINT64: return "uint64"; |
| case TSF_TK_INTEGER: return "integer"; |
| case TSF_TK_LONG: return "long"; |
| case TSF_TK_FLOAT: return "float"; |
| case TSF_TK_DOUBLE: return "double"; |
| default: return "unknown"; |
| } |
| } |
| |
| /* given a kind code, gives you the textual lower-case name */ |
| static TSF_inline |
| const char* tsf_type_kind_lc_name(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_ARRAY: return "array"; |
| case TSF_TK_STRUCT: return "struct"; |
| case TSF_TK_BIT: return "bit"; |
| case TSF_TK_CHOICE: return "choice"; |
| case TSF_TK_VOID: return "void"; |
| case TSF_TK_STRING: return "string"; |
| case TSF_TK_ANY: return "any"; |
| default: return tsf_primitive_type_kind_lc_name(kind_code); |
| } |
| } |
| |
| /* given a primitive kind code, gives you the ntoh/hton name that |
| * we use for it. */ |
| static TSF_inline |
| const char* tsf_primitive_type_kind_nh_name(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_INT8: return "c"; |
| case TSF_TK_UINT8: return "c"; |
| case TSF_TK_INT16: return "s"; |
| case TSF_TK_UINT16: return "s"; |
| case TSF_TK_INT32: return "l"; |
| case TSF_TK_UINT32: return "l"; |
| case TSF_TK_INT64: return "ll"; |
| case TSF_TK_UINT64: return "ll"; |
| case TSF_TK_INTEGER: return "l"; |
| case TSF_TK_LONG: return "ll"; |
| case TSF_TK_FLOAT: return "f"; |
| case TSF_TK_DOUBLE: return "d"; |
| default: return "unknown"; |
| } |
| } |
| |
| /* does a primitive instance of. one primitive is instance of another |
| * if the domain of the first is contained in the domain of the second. |
| * if you wish to assign one primitive to another, then to avoid |
| * a loss of significant or an overflow, you must ensure that the |
| * source is instance of the destination. */ |
| tsf_bool_t tsf_primitive_type_kind_instanceof(tsf_type_kind_t one, |
| tsf_type_kind_t two); |
| |
| /* returns a primitive type kind that matches the requires specification. */ |
| tsf_type_kind_t tsf_primitive_type_kind_for_spec(int32_t lower_bound, |
| int32_t upper_bound, |
| int32_t precision); |
| |
| /* returns the smallest common subtype of the two types, or void if |
| * a common subtype does not exist. */ |
| tsf_type_kind_t tsf_primitive_type_kind_mix(tsf_type_kind_t one, |
| tsf_type_kind_t two); |
| |
| /* tells you if types with the given kind type are containers. */ |
| static TSF_inline |
| tsf_bool_t tsf_type_kind_is_container(tsf_type_kind_t kind_code) { |
| switch (kind_code) { |
| case TSF_TK_ARRAY: |
| case TSF_TK_STRUCT: |
| case TSF_TK_CHOICE: return tsf_true; |
| default: return tsf_false; |
| } |
| } |
| |
| /* tells you if types with the given kind type can store comments. */ |
| static TSF_inline |
| tsf_bool_t tsf_type_kind_is_commentable(tsf_type_kind_t kind_code) { |
| return tsf_type_kind_is_container(kind_code); |
| } |
| |
| /* create a type table (you should almost never have to do this |
| * directly.) */ |
| tsf_type_table_t* tsf_type_table_create(void); |
| |
| /* copy the contents of one type table into another */ |
| tsf_bool_t tsf_type_table_copy(tsf_type_table_t *dest, |
| tsf_type_table_t *src); |
| |
| /* destroy a type table */ |
| void tsf_type_table_destroy(tsf_type_table_t *tt); |
| |
| /* write the type table */ |
| tsf_bool_t tsf_type_table_write(tsf_type_table_t *tt, |
| tsf_writer_t writer, |
| void *arg); |
| |
| /* append a type to the type table */ |
| tsf_bool_t tsf_type_table_append(tsf_type_table_t *tt, |
| const char *name, |
| tsf_type_t *type); |
| |
| /* remove a type from the type table. note that this changes the |
| * indexing of the type table if this isn't the last element. |
| * the only error that this could report is TSF_E_ELE_NOT_FOUND |
| * if the element wasn't in the table. */ |
| tsf_bool_t tsf_type_table_remove(tsf_type_table_t *tt, |
| const char *name); |
| |
| /* find a named type node in the table using the name. */ |
| tsf_named_type_t* tsf_type_table_find_by_name(tsf_type_table_t *tt, |
| const char *name); |
| |
| /* find a named type node in the table using the index. */ |
| tsf_named_type_t* tsf_type_table_find_by_index(tsf_type_table_t *tt, |
| uint32_t index); |
| |
| /* get the number of elements in the type table. */ |
| uint32_t tsf_type_table_get_num_elements(tsf_type_table_t *tt); |
| |
| /* get the hash for a type table. you are guaranteed that this is just |
| * the sum of the hash codes of the constituent tsf_named_type_t objects. |
| * hence, you can always determine what the hash code is after an |
| * apppend operation. */ |
| int tsf_type_table_get_hash(tsf_type_table_t *tt); |
| |
| /* determine is the type table contains any dynamic types. */ |
| tsf_bool_t tsf_type_table_contains_dynamic(tsf_type_table_t *tt); |
| |
| /* compare two type tables. */ |
| tsf_bool_t tsf_type_table_compare(tsf_type_table_t *a, |
| tsf_type_table_t *b); |
| |
| /* create a table of types. */ |
| tsf_type_in_map_t* tsf_type_in_map_create(void); |
| |
| /* create a table of types inside of the provided region. */ |
| tsf_type_in_map_t* tsf_type_in_map_create_in(void *region); |
| |
| /* return the global empty table of types. this one should never |
| * be modified. */ |
| tsf_type_in_map_t* tsf_type_in_map_get_empty_singleton(void); |
| |
| /* clone a table of types. */ |
| tsf_type_in_map_t* tsf_type_in_map_clone(tsf_type_in_map_t *types); |
| |
| /* clone a table of types, creating the new one in a region. */ |
| tsf_type_in_map_t* tsf_type_in_map_clone_in(tsf_type_in_map_t *types, |
| void *region); |
| |
| /* destroy a table of types. has no effect if passed the |
| * empty singleton. cannot be used on region-allocated tables. */ |
| void tsf_type_in_map_destroy(tsf_type_in_map_t *types); |
| |
| /* prepare some number of slots for types in the table. a call to this |
| * function serves as a hint and so does not have any guaranteed effect. |
| * as such, success in this function should not be taken to mean that |
| * the subsequent append operations are guaranteed to succeed and |
| * failure in this operation should not be taken to mean that any |
| * number of subsequent appends should fail. |
| * |
| * (In other words, the return value can be safely ignored. It is there |
| * to aid in debugging only.) */ |
| tsf_bool_t tsf_type_in_map_prepare(tsf_type_in_map_t *types, |
| uint32_t num); |
| |
| /* append a type to the table. the index will be the number of |
| * types prior to the append. */ |
| tsf_bool_t tsf_type_in_map_append(tsf_type_in_map_t *types, |
| tsf_type_t *type); |
| |
| /* returns the number of types in the table. */ |
| uint32_t tsf_type_in_map_get_num_types(tsf_type_in_map_t *types); |
| |
| /* returns the type at the given index in the table. */ |
| tsf_type_t* tsf_type_in_map_get_type(tsf_type_in_map_t *types, |
| uint32_t index); |
| |
| /* reduces the size of the type table to the specified new size. |
| * unpredictably horrible things will happen if the new size is |
| * greater than the current size. */ |
| void tsf_type_in_map_truncate(tsf_type_in_map_t *types, |
| uint32_t new_size); |
| |
| /* compare two type tables. two type tables are equal if they |
| * have the same number of types, and for each index, of the types |
| * at that index are the same according to tsf_type_compare(). */ |
| tsf_bool_t tsf_type_in_map_compare(tsf_type_in_map_t *a, |
| tsf_type_in_map_t *b); |
| |
| /* creates a new empty type map. */ |
| tsf_type_out_map_t* tsf_type_out_map_create(void); |
| |
| /* destroy a type map. */ |
| void tsf_type_out_map_destroy(tsf_type_out_map_t *type_map); |
| |
| /* look up a type code asserting that it must be found. an error is |
| * returned if it is not found. */ |
| tsf_bool_t tsf_type_out_map_find_type_code(tsf_type_out_map_t *type_map, |
| tsf_type_t *type, |
| uint32_t *type_code); |
| |
| /* look up a type code, potentially creating a new one. fails only if |
| * a type code had to be created AND that creation failed. */ |
| tsf_bool_t tsf_type_out_map_get_type_code(tsf_type_out_map_t *type_map, |
| tsf_type_t *type, |
| uint32_t *type_code, |
| tsf_bool_t *created); |
| |
| /* destroy a type out map and produce a type in map thingy from it. */ |
| tsf_type_in_map_t* tsf_type_in_map_from_type_out_map( |
| tsf_type_out_map_t *type_map); |
| |
| /* destroy a type out map and produce a type in map thingy from it. |
| * creates the type in thingy in the provided region. */ |
| tsf_type_in_map_t* tsf_type_in_map_from_type_out_map_in( |
| tsf_type_out_map_t *type_map, |
| void *region); |
| |
| /* create an empty random-access type manager. */ |
| tsf_ra_type_man_t* tsf_ra_type_man_create(void); |
| |
| /* parse a random-access type manager. */ |
| tsf_ra_type_man_t* tsf_ra_type_man_read(tsf_reader_t reader, |
| void *arg); |
| |
| /* destroy a random-access type manager. */ |
| void tsf_ra_type_man_destroy(tsf_ra_type_man_t *man); |
| |
| /* write the stuff in the type manager out to somewhere. */ |
| tsf_bool_t tsf_ra_type_man_write(tsf_ra_type_man_t *man, |
| tsf_writer_t writer, |
| void *arg); |
| |
| /* inset a type into the type manager. do this only if you know |
| that the type is not already in the type manager. */ |
| tsf_bool_t tsf_ra_type_man_insert(tsf_ra_type_man_t *man, |
| tsf_type_t *type, |
| uint32_t type_code, |
| uint64_t ref_count); |
| |
| /* remove a type from the type manager. do this only if you know |
| that the type is in the type manager. */ |
| void tsf_ra_type_man_remove(tsf_ra_type_man_t *man, |
| tsf_type_t *type); |
| |
| /* find a type in the type manager using the type code. if it's |
| not found, NULL will be retorned and an error code will be set. */ |
| tsf_type_t* tsf_ra_type_man_find(tsf_ra_type_man_t *man, |
| uint32_t type_code); |
| |
| /* determine if the type manager has the given type. */ |
| tsf_bool_t tsf_ra_type_man_has_type(tsf_ra_type_man_t *man, |
| tsf_type_t *type); |
| |
| /* get the type code for a type. only call this if you know that |
| the type manager has the type. */ |
| uint32_t tsf_ra_type_man_get_type_code(tsf_ra_type_man_t *man, |
| tsf_type_t *type); |
| |
| /* increase the reference count for the given type. if the type |
| is not in the type manager, it is added to the type manager, |
| assigned a type code, and given a reference count of 1. */ |
| tsf_bool_t tsf_ra_type_man_get_reference(tsf_ra_type_man_t *man, |
| tsf_type_t *type); |
| |
| /* decrease the reference count for a given type. if the reference |
| count drops to 0, the type is removed. */ |
| void tsf_ra_type_man_remove_reference(tsf_ra_type_man_t *man, |
| tsf_type_t *type); |
| |
| uint32_t tsf_ra_type_man_num_types(tsf_ra_type_man_t *man); |
| |
| /* determine if the type manager contains all of the types used by |
| the given buffer. */ |
| tsf_bool_t tsf_ra_type_man_has_buf_types(tsf_ra_type_man_t *man, |
| tsf_buffer_t *buf); |
| |
| /* increase the reference count for the types in the given buffer. |
| creates any types that don't exist. */ |
| tsf_bool_t tsf_ra_type_man_get_buf_references(tsf_ra_type_man_t *man, |
| tsf_buffer_t *buf); |
| |
| /* decrease the reference count for the types in the given buffer. */ |
| void tsf_ra_type_man_remove_buf_references(tsf_ra_type_man_t *man, |
| tsf_buffer_t *buf); |
| |
| /* calculate the number of type codes on the free-list. */ |
| uint32_t tsf_ra_type_man_get_type_code_free_list_size( |
| tsf_ra_type_man_t *man); |
| |
| /* create a type object. this function will not accept TSF_TK_ARRAY. |
| * if you want to create a type with kind ccode TSF_TK_ARRAY, use |
| * tsf_type_create_array(). |
| * |
| * this can only fail due to memory allocation failure or if the kind_code |
| * is invalid. */ |
| tsf_type_t* tsf_type_create(tsf_type_kind_t kind_code); |
| |
| /* create an array type object. |
| * |
| * this can only fail due to memory allocation failure or if element_type is |
| * NULL, in which case it returns without setting error. */ |
| tsf_type_t* tsf_type_create_array(tsf_type_t *element_type); |
| |
| /* create a struct type all at once. note that you can use |
| * tsf_type_create() in combination with tsf_struct_type_append() to do |
| * what this method does. also, you can safely use |
| * tsf_struct_type_append() on the type returned from this method. |
| * |
| * the way you would actually use this is to pass struct element names |
| * paired with types. for example: |
| * |
| * tsf_type_t *my_type = |
| * tsf_type_create_struct("x", |
| * tsf_type_create(TSF_TK_FLOAT), |
| * "y", |
| * tsf_type_create(TSF_TK_FLOAT), |
| * NULL); |
| * |
| * note that you must pass NULL after the last pair. otherwise you'll |
| * get random crashes and stuff. |
| * |
| * the types passed to this function become owned by the function |
| * regardless of success or error. */ |
| tsf_type_t* tsf_type_create_struct(const char *first_name, |
| ...); |
| |
| /* the same as the above but tailored to allow you to use it more easily |
| * from your own subrutines that use variable args. */ |
| tsf_type_t* tsf_type_create_structv(const char *first_name, |
| va_list lst); |
| |
| /* create a choice type all at once. note that you can use |
| * tsf_type_create() in combination with tsf_choice_type_append() to do |
| * what this method does. also, you can safely use |
| * tsf_choice_type_append() on the type returned from this method. |
| * |
| * the way you would actually use this is to pass struct element names |
| * paired with types. for example: |
| * |
| * tsf_type_t *my_type = |
| * tsf_type_create_choice("x", |
| * tsf_type_create(TSF_TK_FLOAT), |
| * "y", |
| * tsf_type_create(TSF_TK_FLOAT), |
| * NULL); |
| * |
| * note that you must pass NULL after the last pair. otherwise you'll |
| * get random crashes and stuff. |
| * |
| * the types passed to this function become owned by the function |
| * regardless of success or error. */ |
| tsf_type_t* tsf_type_create_choice(const char *first_name, |
| ...); |
| |
| /* the same as the above but tailored to allow you to use it more easily |
| * from your own subrutines that use variable args. */ |
| tsf_type_t* tsf_type_create_choicev(const char *first_name, |
| va_list lst); |
| |
| /* read a type. */ |
| tsf_type_t* tsf_type_read(tsf_reader_t reader, |
| void *arg, |
| tsf_limits_t *limits); |
| |
| /* write a type. */ |
| tsf_bool_t tsf_type_write(tsf_type_t *type, |
| tsf_writer_t writer, |
| void *arg); |
| |
| /* duplicate a type. this gives you a copy of the type that must be |
| * separately destroyed. so, if you wish to retain the type after a |
| * call to a function that might destroy it, call this first. this |
| * will only return NULL if the type you pass it is NULL, for the |
| * purpose of error propagation. this will never create an error. */ |
| tsf_type_t* tsf_type_dup(tsf_type_t *type); |
| |
| /* destroy a type object. */ |
| void tsf_type_destroy(tsf_type_t *type); |
| |
| /* get the size in bytes of the type. note that this operation cannot |
| * fail. note further that the size is lazily computed, so the first time |
| * you call this, it will be expensive, but it will become cheap for all |
| * subsequent calls. */ |
| uint32_t tsf_type_get_byte_size(tsf_type_t *type); |
| |
| /* verify a type against some limits; this is a fairly costly process, |
| * probably at least as expensive as writing or reading a type. tsf_true |
| * is returned if the type is OK. if tsf_false is returned, |
| * TSF_E_NOT_ALLOWED is set. if limits is NULL, all types should |
| * verify */ |
| tsf_bool_t tsf_type_verify(tsf_type_t *type, |
| tsf_limits_t *limits); |
| |
| /* get a hash code for a type. note that if you want to hash types, you |
| * may benefit from memoizing the type first; then you can use a pointer |
| * hash. */ |
| int tsf_type_get_hash(tsf_type_t *type); |
| |
| /* memoize the given type and return the memo master. the memo master |
| * for two types for ehich tsf_type_compare returns true should be |
| * identical, i.e. point to the same tsf_type object. this does not |
| * consume the type you pass it. the returned type is not owned by you |
| * and is only guaranteed to stay alive as long as you don't destroy |
| * the type you passed. therefore you might want to do: |
| * |
| * memo_type = tsf_type_dup(tsf_type_memo(type)); |
| * |
| * if you want to be safe. if you want to relinquish ownership of the |
| * original type and have ownership of the memo, you probably want: |
| * |
| * memo_type = tsf_type_dup(tsf_type_memo(type)); |
| * tsf_type_destroy(type); |
| * |
| * note that it's possible for tsf_type_memo() to simply return the |
| * type it was passed, if that is the only extant type that looks like |
| * that. also note that memoization ignores the native mapping, so |
| * using the above memo-and-relinquish dance is not useful for types |
| * that have native mapping. however, it's still useful to memoize such |
| * types if you want to speed up hashing and comparing. */ |
| tsf_type_t* tsf_type_memo(tsf_type_t *type); |
| |
| /* determine if the type is dynamic (that is, if it has any anies in |
| it) */ |
| tsf_bool_t tsf_type_is_dynamic(tsf_type_t *type); |
| |
| /* compare two types. neither type becomes owned by the function; both |
| * are still your responsibility after the call. returns tsf_true |
| * if the types are equal, tsf_false otherwise. */ |
| tsf_bool_t tsf_type_compare(tsf_type_t *a, tsf_type_t *b); |
| |
| /* determines if the first type is an instance of the second type. |
| * the instanceof relationship is defined in a big comment up above |
| * the definition of the tsf_type_kind enumeration. */ |
| tsf_bool_t tsf_type_instanceof(tsf_type_t *one, |
| tsf_type_t *two); |
| |
| /* get the kind code for a type object. */ |
| tsf_type_kind_t tsf_type_get_kind_code(tsf_type_t *type); |
| |
| /* if the size of the type is known statically, it returns it. otherwise, |
| * it returns UINT32_MAX. */ |
| uint32_t tsf_type_get_static_size(tsf_type_t *type); |
| |
| /* tells you the comment on the type. may be NULL if there is no comment |
| * or if the type is not commentable. */ |
| const char* tsf_type_get_comment(tsf_type_t *type); |
| |
| /* tells you if the type has a comment */ |
| tsf_bool_t tsf_type_has_comment(tsf_type_t *type); |
| |
| /* sets the comment on the type. will return TSF_E_BAD_TYPE_KIND if the |
| * type is not commentable. */ |
| tsf_bool_t tsf_type_set_comment(tsf_type_t **type, |
| const char *comment); |
| |
| /* tells you the name of the type. may be NULL if there is no name or if |
| * the type is not commentable. */ |
| const char* tsf_type_get_name(tsf_type_t *type); |
| |
| /* tells you if the type has a name. */ |
| tsf_bool_t tsf_type_has_name(tsf_type_t *type); |
| |
| /* sets the name of the type. will return TSF_E_BAD_TYPE_KIND if the |
| * type is not commentable. */ |
| tsf_bool_t tsf_type_set_name(tsf_type_t **type, |
| const char *name); |
| |
| /* tells you if the version is non-zero (i.e. not equal to 0.0.0) */ |
| tsf_bool_t tsf_type_has_version(tsf_type_t *type); |
| |
| /* tells you the raw version of the type. */ |
| uint32_t tsf_type_get_raw_version(tsf_type_t *type); |
| |
| /* tells you the major version of the type. */ |
| uint16_t tsf_type_get_major_version(tsf_type_t *type); |
| |
| /* tells you the minor version of the type. */ |
| uint8_t tsf_type_get_minor_version(tsf_type_t *type); |
| |
| /* tells you the patch version of the type. */ |
| uint8_t tsf_type_get_patch_version(tsf_type_t *type); |
| |
| /* sets the raw version of the type. will return TSF_E_BAD_TYPE_KIND if the |
| * type is not commentable. */ |
| tsf_bool_t tsf_type_set_raw_version(tsf_type_t **type, |
| uint32_t version); |
| |
| /* sets the version of the type. will return TSF_E_BAD_TYPE_KIND if the |
| * type is not commentable. */ |
| tsf_bool_t tsf_type_set_version(tsf_type_t **type, |
| uint16_t major, |
| uint8_t minor, |
| uint8_t patch); |
| |
| /* get the element type of an array type object. the returned |
| * object is not owned by you; it is owned by the array type. */ |
| tsf_type_t* tsf_array_type_get_element_type(tsf_type_t *type); |
| |
| /* append a new field to a struct type. the string argument |
| * does not become owned by the function; it is still your |
| * responsibility to free it. note that the string cannot |
| * exceed TSF_STR_SIZE; if it does, an error will be returned. |
| * this will propagate errors on the *ele_type. |
| * |
| * this only fails if memory allocation fails, if the name string |
| * is too long, or ele_type is NULL. */ |
| tsf_bool_t tsf_struct_type_append(tsf_type_t **type, |
| const char *name, |
| tsf_type_t *ele_type); |
| |
| /* set the 'optional' attribute of a structure field. */ |
| tsf_bool_t tsf_struct_type_set_optional(tsf_type_t **type, |
| const char *name, |
| tsf_bool_t optional); |
| |
| /* set the comment for a structure field. */ |
| tsf_bool_t tsf_struct_type_set_comment(tsf_type_t **type, |
| const char *name, |
| const char *comment); |
| |
| /* remove a struct field. note that this changes the ordering of |
| * fields. */ |
| tsf_bool_t tsf_struct_type_remove(tsf_type_t **type, |
| const char *name); |
| |
| /* find an element in a struct type. this search should be |
| * fast, as it uses a hashtable. */ |
| tsf_type_t* tsf_struct_type_find(tsf_type_t *type, |
| const char *name); |
| |
| /* find the pair element in a struct type; the pair |
| * element contains more info. this search should be fast, |
| * as it uses a hashtable. */ |
| tsf_named_type_t* tsf_struct_type_find_node(tsf_type_t *type, |
| const char *name); |
| |
| /* returns the number of elements in the struct. */ |
| uint32_t tsf_struct_type_get_num_elements(tsf_type_t *type); |
| |
| /* get the ith element in the struct */ |
| tsf_named_type_t *tsf_struct_type_get_element(tsf_type_t *type, |
| uint32_t index); |
| |
| /* get the name of the ith element in the struct */ |
| const char* tsf_struct_type_get_element_name(tsf_type_t *type, |
| uint32_t index); |
| |
| /* get the type of the ith element in the struct */ |
| tsf_type_t* tsf_struct_type_get_element_type(tsf_type_t *type, |
| uint32_t index); |
| |
| /* append a new field to a choice type. the string argument |
| * does not become owned by the function; it is still your |
| * responsibility to free it. note that the string cannot |
| * exceed TSF_STR_SIZE; if it does, an error will be returned. |
| * this will propagate errors on the *ele_type. */ |
| tsf_bool_t tsf_choice_type_append(tsf_type_t **type, |
| const char *name, |
| tsf_type_t *ele_type); |
| |
| /* set the comment for a choice field. */ |
| tsf_bool_t tsf_choice_type_set_comment(tsf_type_t **type, |
| const char *name, |
| const char *comment); |
| |
| /* find an element in a choice type. this search should be |
| * fast, as it uses a hashtable. */ |
| tsf_type_t* tsf_choice_type_find(tsf_type_t *type, |
| const char *name); |
| |
| /* find the pair element in a choice type; the pair |
| * element contains more info. this search should be fast, |
| * as it uses a hashtable. */ |
| tsf_named_type_t* tsf_choice_type_find_node(tsf_type_t *type, |
| const char *name); |
| |
| /* returns the number of elements in the choice. */ |
| uint32_t tsf_choice_type_get_num_elements(tsf_type_t *type); |
| |
| /* get the ith element in the choice */ |
| tsf_named_type_t* tsf_choice_type_get_element(tsf_type_t *type, |
| uint32_t index); |
| |
| /* get the name of the ith element in the choice */ |
| const char* tsf_choice_type_get_element_name(tsf_type_t *type, |
| uint32_t index); |
| |
| /* get the type of the ith element in the choice */ |
| tsf_type_t* tsf_choice_type_get_element_type(tsf_type_t *type, |
| uint32_t index); |
| |
| /* returns true if the choice has any non-void types. */ |
| tsf_bool_t tsf_choice_type_has_non_void(tsf_type_t *type); |
| |
| /* returns true if: |
| * -> the latter choice type has at least as many elements as the |
| * first, and |
| * -> for each of the elements in the first choice type, the element |
| * in the second choice type that has the same index also has the |
| * same name. |
| * |
| * note that the element types do not have to match for this |
| * predicate to hold. however, if you are interested in adding an |
| * instanceof constraint for the corresponding elements of the two |
| * choice types, you can do so by performing an instanceof test on |
| * a and b. */ |
| tsf_bool_t tsf_choice_type_uses_same_indices(tsf_type_t *a, |
| tsf_type_t *b); |
| |
| /* creates a named type object with the given name, type, and index. |
| * failure to create this thing results in the type being destroyed. |
| * the has_offset field is set to false, the offset field is set to 0, |
| * and the comment field is set to NULL. */ |
| tsf_named_type_t* tsf_named_type_create(const char *name, |
| tsf_type_t *type, |
| uint32_t index); |
| |
| /* destroy a named type object. destroys the type. frees the |
| * comment if there is one. */ |
| void tsf_named_type_destroy(tsf_named_type_t *node); |
| |
| /* returns the name of the type in the node. what gets returned |
| * should be treated as a constant string that you do not own. */ |
| const char* tsf_named_type_get_name(tsf_named_type_t *node); |
| |
| /* returns the type that the node holds. what gets returned is |
| * of type tsf_type_t* and should be treated as constant that you |
| * do not own. */ |
| tsf_type_t* tsf_named_type_get_type(tsf_named_type_t *node); |
| |
| /* returns the index of a named type node within its structure |
| * type. */ |
| uint32_t tsf_named_type_get_index(tsf_named_type_t *node); |
| |
| /* set the optional attribute */ |
| tsf_bool_t tsf_named_type_get_optional(tsf_named_type_t *node); |
| |
| /* set the optional attribute */ |
| void tsf_named_type_set_optional(tsf_named_type_t *node, |
| tsf_bool_t optional); |
| |
| /* returns the comment of a names type. may be NULL, indicating that |
| * no comment was ever set */ |
| const char* tsf_named_type_get_comment(tsf_named_type_t *node); |
| |
| /* tells you if the named type has a comment */ |
| tsf_bool_t tsf_named_type_has_comment(tsf_named_type_t *node); |
| |
| /* set the comment on a named type. deletes the old one if |
| * necessary. */ |
| tsf_bool_t tsf_named_type_set_comment(tsf_named_type_t *node, |
| const char *comment); |
| |
| /* get the hash code for the named type. you are guaranteed that types |
| * of type TSF_TK_STRUCT and TSF_TK_CHOICE will use the sum of these hash |
| * codes as part of computing their own hash code. you're guaranteed |
| * that tsf_type_table_table_t will use the sum of these hash codes as |
| * part of computing their own hash code. */ |
| int tsf_named_type_get_hash(tsf_named_type_t *node); |
| |
| /* create a buffer. the type is duped by the buffer. the types table |
| * becomes owned. and, to make things more interesting, the boolean |
| * argument determines if the void* is owned by the buffer. if it is |
| * owned by the buffer, then if this function fails, the buffer will be |
| * freed. */ |
| tsf_buffer_t* tsf_buffer_create(tsf_type_t *type, |
| tsf_type_in_map_t *types, |
| void *data, |
| uint32_t size, |
| tsf_bool_t keep); |
| |
| /* create a buffer in a region. exactly like tsf_buffer_create() |
| * except that the buffer is created in a region. it is assumed |
| * that the void* data is allocated in the same region and that |
| * tsf_type_in_map_t* is, too. note that the tsf_bool_t argument |
| * is omitted since tsf_buffer_destroy() is not applicable to |
| * region-allocated buffers. */ |
| tsf_buffer_t* tsf_buffer_create_in(tsf_type_t *type, |
| tsf_type_in_map_t *types, |
| void *data, |
| uint32_t size, |
| void *region); |
| |
| /* get the empty buffer singleton. */ |
| tsf_buffer_t* tsf_buffer_get_empty_singleton(void); |
| |
| /* clone the buffer. the resulting buffer has the keep bit set to |
| * true. note that this is a deep clone, so that the actual data |
| * itself is also copied. this means that this operation, and any |
| * operations on the new buffer have no effect on the original buffer. */ |
| tsf_buffer_t* tsf_buffer_clone(tsf_buffer_t *buf); |
| |
| /* clone the buffer into the given region. exactly like |
| * tsf_buffer_clone() except for the region part. */ |
| tsf_buffer_t* tsf_buffer_clone_in(tsf_buffer_t *buf, |
| void *region); |
| |
| /* duplicate a buffer. this will give you access to the given buffer |
| * until you call tsf_buffer_destroy on it. if the buffer was heap- |
| * allocated and has the keep bit set to true, this operation will be |
| * very fast - it will increment a count and maybe do some simple |
| * locking/unlocking. if the buffer was region allocated or if the |
| * keep bit was set to false, then this has the same cost as |
| * tsf_buffer_clone(). as such, you must do error checking after a |
| * a call to tsf_buffer_dup(). */ |
| tsf_buffer_t* tsf_buffer_dup(tsf_buffer_t *buf); |
| |
| /* destroy a buffer. cannot be used on region allocated buffers. */ |
| void tsf_buffer_destroy(tsf_buffer_t *buf); |
| |
| /* tells you the size of the buffer */ |
| uint32_t tsf_buffer_get_size(tsf_buffer_t *buf); |
| |
| /* gives you the data payload of the buffer */ |
| const void* tsf_buffer_get_data(tsf_buffer_t *buf); |
| |
| /* read a buffer from another buffer and associate it with the given |
| * type. this is also a bit of a trusted routine... |
| * if the region pointer is non-NULL, then the resulting buffer will |
| * be region-allocated. otherwise it will be malloc-allocated and |
| * you will be expected to call tsf_buffer_destroy() at some point. */ |
| tsf_buffer_t* tsf_buffer_read_from_buf(tsf_type_in_map_t *enclosing_types, |
| uint8_t **cur, |
| uint8_t *end, |
| void *region); |
| |
| /* skip a buffer (in a buffer, not on disk). takes care of skipping the |
| translation table if dealing with a dynamic type. */ |
| tsf_bool_t tsf_buffer_skip_in_buf(tsf_type_in_map_t *enclosing_types, |
| uint8_t **cur, |
| uint8_t *end); |
| |
| /* calculate the size that would be required to write this buffer into |
| * another buffer. */ |
| uint32_t tsf_buffer_calc_size(tsf_buffer_t *buf); |
| |
| /* write a buffer into another buffer */ |
| tsf_bool_t tsf_buffer_write_to_buf(tsf_buffer_t *buf, |
| tsf_type_out_map_t *type_map, |
| uint8_t **target); |
| |
| /* write a buffer and its types to the given writer. this is not an |
| * optimized operation, but it should be fine if the size of the buffer's |
| * data is large compared to the size of the type. */ |
| tsf_bool_t tsf_buffer_write_whole(tsf_buffer_t *buf, |
| tsf_writer_t writer, |
| void *arg); |
| |
| /* read a buffer and its types from the given reader. */ |
| tsf_buffer_t* tsf_buffer_read_whole(tsf_reader_t reader, |
| void *arg, |
| tsf_limits_t *limits); |
| |
| /* get the SHA1 hash of the whole buffer (including types). this is not |
| * an optimized operation. note that the result is placed in the |
| * uint32_t* buffer, which must be big enough to hold 5 uint32_t's. */ |
| tsf_bool_t tsf_buffer_calc_sha1(tsf_buffer_t *buf, |
| uint32_t *result); |
| |
| /* compare two buffers. two buffers are equal if they contain exactly |
| * the same payload bytes, if their types are the same according to |
| * tsf_type_compare(), and if their type tables are the same according |
| * to tsf_type_in_map_compare(). */ |
| tsf_bool_t tsf_buffer_compare(tsf_buffer_t *a, |
| tsf_buffer_t *b); |
| |
| /* given a buffer, gives you the type. */ |
| tsf_type_t* tsf_buffer_get_type(tsf_buffer_t *buf); |
| |
| /* given a buffer, tells you if its type is an instance of another type */ |
| tsf_bool_t tsf_buffer_instanceof(tsf_buffer_t *buf, |
| tsf_type_t *type); |
| |
| /* given a buffer, gives you the shared types. */ |
| tsf_type_in_map_t* tsf_buffer_get_shared_types(tsf_buffer_t *buf); |
| |
| /* get the size of a bitvector that has some number of bits. */ |
| static TSF_inline |
| size_t tsf_sizeof_bitvector(uint32_t num_bits) { |
| return (num_bits + 7) >> 3; |
| } |
| |
| /* set a bit in a bitvector. */ |
| static TSF_inline |
| void tsf_bitvector_set(tsf_native_bitvector_t *bitvector, |
| size_t bit_index, |
| tsf_bool_t value) { |
| uint8_t *byte = bitvector->bits + (bit_index >> 3); |
| uint8_t bit_mask = 1 << (bit_index & 7); |
| if (value) |
| *byte |= bit_mask; |
| else |
| *byte &= ~bit_mask; |
| } |
| |
| /* get a bit from a bitvector. */ |
| static TSF_inline |
| tsf_bool_t tsf_bitvector_get(tsf_native_bitvector_t *bitvector, |
| size_t bit_index) { |
| uint8_t *byte = bitvector->bits + (bit_index >> 3); |
| uint8_t bit_mask = 1 << (bit_index & 7); |
| return (*byte & bit_mask) != 0; |
| } |
| |
| /* create an serial input manager. a serial input manager expects zero or |
| * more type headers to precede data. instances of this type are not |
| * thread-safe; if you want to use one from multiple threads then you need |
| * to use locking around all calls. */ |
| tsf_serial_in_man_t* tsf_serial_in_man_create(tsf_reader_t reader, |
| void *arg, |
| tsf_limits_t *limits); |
| |
| /* destroy an input manager. */ |
| void tsf_serial_in_man_destroy(tsf_serial_in_man_t *in_man); |
| |
| /* force the serial input manager to read a TSF_SP_C_RESET_TYPES command. |
| * if this command is not what is found in the input stream, an error is |
| * reported. provided that a TSF_SP_C_RESET_TYPES command is in fact |
| * found, the type mapping will be reset. |
| * |
| * will set error to TSF_E_UNEXPECTED_EOF if there weren't enough bytes for |
| * a TSF_SP_C_RESET_TYPES command. |
| * |
| * will set error to TSF_E_PARSE_ERROR if the bytes read were not the |
| * currect ones (effectively, this is complaining of a bad magic number).*/ |
| tsf_bool_t tsf_serial_in_man_read_reset_types(tsf_serial_in_man_t *in_man); |
| |
| /* take the given file descriptor and read the reset types command from |
| * it. returns true if there was a reset types command and false on |
| * error. returns the same errors as tsf_serial_in_man_read_reset_types() |
| * unless something really weird happens. */ |
| tsf_bool_t tsf_serial_in_man_read_reset_types_from_fd(int fd); |
| |
| /* take the given file, open it, and invoke |
| * tsf_serial_in_man_read_reset_types_from_fd on it. */ |
| tsf_bool_t tsf_serial_in_man_read_reset_types_from_file( |
| const char *filename); |
| |
| /* register a callback that will get called every time the input manager |
| * encounters a new type. */ |
| tsf_bool_t tsf_serial_in_man_register_type_cback( |
| tsf_serial_in_man_t *in_man, |
| tsf_type_cback_t cback, |
| void *arg); |
| |
| /* unregister all type callbacks that match the given function pointer |
| * and void* argument. */ |
| uint32_t tsf_serial_in_man_unregister_type_cback( |
| tsf_serial_in_man_t *in_man, |
| tsf_type_cback_t cback, |
| void *arg); |
| |
| /* read data using the serial input manager. */ |
| tsf_buffer_t* tsf_serial_in_man_read(tsf_serial_in_man_t *in_man); |
| |
| /* get the state of the input manager right now. if you are using random |
| * access files and you wish to seek back to the position you're in now, |
| * you also need to save the state of the input manager and restore it |
| * right after the seek. */ |
| tsf_serial_in_man_state_t tsf_serial_in_man_get_state(tsf_serial_in_man_t *in_man); |
| |
| /* set the state of the input manager. do this whenever you seek the file |
| * that the serial input manager is being used to read. */ |
| void tsf_serial_in_man_set_state(tsf_serial_in_man_t *in_man, |
| tsf_serial_in_man_state_t state); |
| |
| /* create an output manager. instances of this type are not thread-safe; if |
| * you want to use one from multiple threads then you need to lock around |
| * calls. */ |
| tsf_serial_out_man_t* tsf_serial_out_man_create(tsf_writer_t writer, |
| void *arg); |
| |
| /* destroy an output manager. */ |
| void tsf_serial_out_man_destroy(tsf_serial_out_man_t *out_man); |
| |
| tsf_bool_t tsf_serial_out_man_reset_types(tsf_serial_out_man_t *out_man); |
| |
| /* write data using an output manager. this will do error |
| * propagation on *buf. */ |
| tsf_bool_t tsf_serial_out_man_write(tsf_serial_out_man_t *out_man, |
| tsf_buffer_t *buf); |
| |
| /* create a tsf data structure using the given type. the type |
| * gets dupped for the returned structure. this will |
| * do error propagation on *type. |
| * |
| * Note that this function should never be used to create |
| * primitives, bits, voids, or strings. */ |
| tsf_reflect_t* tsf_reflect_create(tsf_type_t *type); |
| |
| /* create a tsf data structure to hold a value of the given type. */ |
| tsf_reflect_t* tsf_reflect_create_int8_t(int8_t value); |
| tsf_reflect_t* tsf_reflect_create_uint8_t(uint8_t value); |
| tsf_reflect_t* tsf_reflect_create_int16_t(int16_t value); |
| tsf_reflect_t* tsf_reflect_create_uint16_t(uint16_t value); |
| tsf_reflect_t* tsf_reflect_create_int32_t(int32_t value); |
| tsf_reflect_t* tsf_reflect_create_uint32_t(uint32_t value); |
| tsf_reflect_t* tsf_reflect_create_int64_t(int64_t value); |
| tsf_reflect_t* tsf_reflect_create_uint64_t(uint64_t value); |
| tsf_reflect_t* tsf_reflect_create_integer(tsf_integer_t value); |
| tsf_reflect_t* tsf_reflect_create_long(tsf_long_t value); |
| tsf_reflect_t* tsf_reflect_create_float(float value); |
| tsf_reflect_t* tsf_reflect_create_double(double value); |
| |
| /* macro that allows one to parameterize the above. */ |
| #define tsf_reflect_create_primitive(type,value) \ |
| (tsf_reflect_create_##type(value)) |
| |
| /* create a tsf data struct to hold a bit with the given value. */ |
| tsf_reflect_t* tsf_reflect_create_bit(tsf_bool_t value); |
| |
| /* create a 'void' data structure; this is just a singleton for |
| * the void type. you'll probably only have to deal with this if |
| * you use the choice type for enumerations. */ |
| tsf_reflect_t* tsf_reflect_create_void(void); |
| |
| /* create a string. the actual char* is duplicated. */ |
| tsf_reflect_t* tsf_reflect_create_string(const char *str); |
| |
| /* create an 'any'. this is a dynamic holder for damn-near |
| * anything. the buffer becomes owned by the reflect object, |
| * so that even if this creation fails, the buffer is no longer |
| * available to you. */ |
| tsf_reflect_t* tsf_reflect_create_any(tsf_buffer_t *buf); |
| |
| /* clone a data structure */ |
| tsf_reflect_t* tsf_reflect_clone(tsf_reflect_t *data); |
| |
| /* destroy a data structure */ |
| void tsf_reflect_destroy(tsf_reflect_t *data); |
| |
| /* produce a buffer from data */ |
| tsf_buffer_t* tsf_reflect_make_buffer(tsf_reflect_t *data); |
| |
| /* produce data from a buffer */ |
| tsf_reflect_t* tsf_reflect_from_buffer(tsf_buffer_t *buf); |
| |
| tsf_type_t* tsf_reflect_get_type(tsf_reflect_t *data); |
| |
| tsf_type_kind_t tsf_reflect_get_kind_code(tsf_reflect_t *data); |
| |
| /* set a structure element. this will do error propagation on |
| * *ele_data. */ |
| tsf_bool_t tsf_struct_reflect_set_element(tsf_reflect_t *data, |
| const char *name, |
| tsf_reflect_t *ele_data); |
| |
| /* set a structure element by index. this will do error propagation |
| on *ele. */ |
| tsf_bool_t tsf_struct_reflect_set_element_by_index(tsf_reflect_t *data, |
| uint32_t index, |
| tsf_reflect_t *ele); |
| |
| /* get a structure element */ |
| tsf_reflect_t* tsf_struct_reflect_get_element(tsf_reflect_t *data, |
| const char *name); |
| |
| /* get a structure element by index */ |
| tsf_reflect_t* tsf_struct_reflect_get_element_by_index(tsf_reflect_t *data, |
| uint32_t index); |
| |
| /* get the number of elements in the structure */ |
| uint32_t tsf_struct_reflect_get_num_elements(tsf_reflect_t *data); |
| |
| /* set the choice. as a convenience shortcut, if choice is NULL, this is |
| equivalent to calling tsf_choice_reflect_set_unknown(). this means |
| that ele_data is ignored. */ |
| tsf_bool_t tsf_choice_reflect_set(tsf_reflect_t *data, |
| const char *choice, |
| tsf_reflect_t *ele_data); |
| |
| /* set the choice by index. as a convenience shortcut, if choice is |
| UINT32_MAX, this is equivalent to calling |
| tsf_choice_reflect_set_unknown(). this means that ele_data is ignored. */ |
| tsf_bool_t tsf_choice_reflect_set_by_index(tsf_reflect_t *data, |
| uint32_t index, |
| tsf_reflect_t *ele_data); |
| |
| /* set the choice to unknown. */ |
| tsf_bool_t tsf_choice_reflect_set_unknown(tsf_reflect_t *data); |
| |
| /* tells you the choice index */ |
| uint32_t tsf_choice_reflect_get_index(tsf_reflect_t *data); |
| |
| /* tells you if the choice is know (not unknown) */ |
| tsf_bool_t tsf_choice_reflect_is_known(tsf_reflect_t *data); |
| |
| /* get the choice. if unknown, will return NULL. */ |
| const char* tsf_choice_reflect_get_choice(tsf_reflect_t *data); |
| |
| /* get the data associated with the choice. if unknown, will |
| * return NULL. */ |
| tsf_reflect_t* tsf_choice_reflect_get_data(tsf_reflect_t *data); |
| |
| /* append an element to the array. this will do error propagation |
| * on *ele_data. */ |
| tsf_bool_t tsf_array_reflect_append(tsf_reflect_t *data, |
| tsf_reflect_t *ele_data); |
| |
| /* get an array element */ |
| tsf_reflect_t* tsf_array_reflect_get_element(tsf_reflect_t *data, |
| uint32_t index); |
| |
| /* get the number of elements in the array */ |
| uint32_t tsf_array_reflect_get_num_elements(tsf_reflect_t *data); |
| |
| /* get the primitives... */ |
| int8_t tsf_int8_t_reflect_get(tsf_reflect_t *data); |
| uint8_t tsf_uint8_t_reflect_get(tsf_reflect_t *data); |
| int16_t tsf_int16_t_reflect_get(tsf_reflect_t *data); |
| uint16_t tsf_uint16_t_reflect_get(tsf_reflect_t *data); |
| int32_t tsf_int32_t_reflect_get(tsf_reflect_t *data); |
| uint32_t tsf_uint32_t_reflect_get(tsf_reflect_t *data); |
| int64_t tsf_int64_t_reflect_get(tsf_reflect_t *data); |
| uint64_t tsf_uint64_t_reflect_get(tsf_reflect_t *data); |
| tsf_integer_t tsf_integer_reflect_get(tsf_reflect_t *data); |
| tsf_long_t tsf_long_reflect_get(tsf_reflect_t *data); |
| float tsf_float_reflect_get(tsf_reflect_t *data); |
| double tsf_double_reflect_get(tsf_reflect_t *data); |
| #define tsf_primitive_reflect_get(data,type) \ |
| (tsf_##type##_reflect_get(data)) |
| |
| /* get a bit */ |
| tsf_bool_t tsf_bit_reflect_get(tsf_reflect_t *data); |
| |
| /* get the string. */ |
| const char* tsf_string_reflect_get(tsf_reflect_t *data); |
| |
| /* get the buffer from an any. */ |
| tsf_buffer_t* tsf_any_reflect_get_buffer(tsf_reflect_t *data); |
| |
| /* returns tsf_true if the given type has a complete structure mapping. |
| * this does not tell you if the given type has a mapping within its |
| * parent type - it only tells you if the types it contains have |
| * mappings. hence, this always returns tsf_true for types that are not |
| * containers. if this function returns tsf_false, then calling any of |
| * the tsf_native functions on this type will result in unpredictable |
| * behavior (unless otherwise noted). */ |
| tsf_bool_t tsf_native_type_has_struct_mapping(tsf_type_t *type); |
| |
| /* get the native size of a particular type within its parent |
| * struct. this can only be called on types that have a struct |
| * mapping. */ |
| size_t tsf_native_type_get_size(tsf_type_t *type); |
| |
| /* get a bound on the total size of the native data structure rooted at |
| * the given type. will return UINTm_MAX if the bound cannot be deduced. */ |
| void tsf_native_type_get_total_size_bounds(tsf_type_t *type, |
| size_t *min, |
| size_t *max); |
| |
| /* returns true if a memcpy() from source to destination with the source's |
| * size will give the 'desired' result. */ |
| tsf_bool_t tsf_native_type_can_blit(tsf_type_t *dest_type, |
| tsf_type_t *src_type); |
| |
| /* returns the offset of a particular named type node. */ |
| size_t tsf_native_named_type_get_offset(tsf_named_type_t *node); |
| |
| /* gives a mapping (offset) for an element within a struct. note |
| * that this function is not particularly fast. however, you shouldn't |
| * care about the speed of this function since you should only ever |
| * call it once for each structure element for the lifetime of your |
| * program. */ |
| tsf_bool_t tsf_native_struct_type_map(tsf_type_t **type, |
| const char *name, |
| size_t offset); |
| |
| /* gives a size to a struct. note that like tsf_native_struct_type_map(), |
| * this function is not particularly fast. */ |
| tsf_bool_t tsf_native_struct_type_set_size(tsf_type_t **type, |
| size_t size); |
| |
| /* creates a struct and sets its native size. equivalent to calling |
| * tsf_type_create(TSF_TK_STRUCT) and then calling |
| * tsf_native_struct_type_set_size(). */ |
| tsf_type_t* tsf_native_struct_type_create(size_t size); |
| |
| /* appends a struct field and sets its offset. equivalent to calling |
| * tsf_struct_type_append() and then tsf_native_struct_type_map(). */ |
| tsf_bool_t tsf_native_struct_type_append(tsf_type_t **type, |
| const char *name, |
| tsf_type_t *ele_type, |
| size_t offset); |
| |
| /* sets the constructor callback that is called just before the fields of |
| * the struct are populated during parsing of copying. this is appropriate |
| * for initializing other fields that the user-defined struct might have. */ |
| tsf_bool_t tsf_native_struct_type_set_constructor(tsf_type_t **type, |
| void (*cons)(void*)); |
| |
| /* sets the destructor callback that is called just before the struct is |
| * deallocated. for region-allocated structs, this gets added as a region |
| * destruction callback. */ |
| tsf_bool_t tsf_native_struct_type_set_pre_destructor(tsf_type_t **type, |
| void (*pre_dest)(void*)); |
| |
| /* sets the destructor callback that is called instead of normal struct |
| * destruction in tsf_destructor. setting this means that destroying the struct |
| * using a destructor will not call the pre_destructor. */ |
| tsf_bool_t tsf_native_struct_type_set_destructor(tsf_type_t **type, |
| void (*dest)(void*)); |
| |
| /* get the constructor to call when the struct gets allocated */ |
| void (*tsf_native_struct_type_get_constructor(tsf_type_t *type))(void*); |
| |
| /* get the destructor to call when the struct gets deallocated */ |
| void (*tsf_native_struct_type_get_pre_destructor(tsf_type_t *type))(void*); |
| |
| /* get the destructor to call when the struct gets deallocated */ |
| void (*tsf_native_struct_type_get_destructor(tsf_type_t *type))(void*); |
| |
| /* set all of the offsets and things required by choice */ |
| tsf_bool_t tsf_native_choice_type_map(tsf_type_t **type, |
| tsf_bool_t in_place, |
| size_t value_offset, |
| size_t data_offset, |
| size_t size); |
| |
| /* creates a choice type and sets all of its offsets. */ |
| tsf_type_t* tsf_native_choice_type_create(tsf_bool_t in_place, |
| size_t value_offset, |
| size_t data_offset, |
| size_t size); |
| |
| /* is the choice mapping in place? */ |
| tsf_bool_t tsf_native_choice_type_is_in_place(tsf_type_t *type); |
| |
| /* retrieve the data offset */ |
| size_t tsf_native_choice_type_get_data_offset(tsf_type_t *type); |
| |
| /* retrieve the value offset */ |
| size_t tsf_native_choice_type_get_value_offset(tsf_type_t *type); |
| |
| /* retrieve the size */ |
| size_t tsf_native_choice_type_get_size(tsf_type_t *type); |
| |
| /* automatically produces some sort of mapping. you probably don't want |
| * to use this method, since you get no guarantee that this will map to |
| * any of your structures. this method's main intent is to aid in the |
| * writing of regression tests. |
| * |
| * if do_align is tsf_true, each element will be aligned according to |
| * the detected native alignment (the alignment necessary for the largest |
| * data type, which is usually determined by either pointers or doubles). |
| * |
| * if do_align is tsf_false, no alignment guarantee is made. you have |
| * to be quite careful when using this option, as it might cause the |
| * GPC programs generated for this type to crash. */ |
| tsf_bool_t tsf_native_type_produce_some_mapping(tsf_type_t **type, |
| tsf_bool_t do_align); |
| |
| /* given a type that has complete struct mapping, produce a generator |
| * that can be used to produce tsf_buffer_t objects from pointers |
| * to the struct. */ |
| tsf_genrtr_t* tsf_generator_create(tsf_type_t *type); |
| |
| /* generate a buffer using some data and a generator. */ |
| tsf_buffer_t* tsf_generator_generate(tsf_genrtr_t *gen, |
| void *data); |
| |
| /* generate a buffer in a region using some data and a generator. */ |
| tsf_buffer_t* tsf_generator_generate_in(tsf_genrtr_t *gen, |
| void *data, |
| void *region); |
| |
| /* destroy the generator. */ |
| void tsf_generator_destroy(tsf_genrtr_t *gen); |
| |
| /* given a type that has a complete struct mapping, produce a parser |
| * that can be used to read tsf_buffer_t objects and produce data |
| * that has the structure you specified in the struct mapping. */ |
| tsf_parser_t* tsf_parser_create(tsf_type_t *type); |
| |
| /* parse a buffer and generate data according to the struct mapping |
| * in the type you gave to tsf_parser_create(). |
| * |
| * stuff returned from this function can be freed using |
| * tsf_region_free() (see tsf_region.h). */ |
| void* tsf_parser_parse(tsf_parser_t *parser, |
| tsf_buffer_t *buffer); |
| |
| /* parse a buffer and generate data according to the struct mapping |
| * in the type you gave to tsf_parser_create(). the data is |
| * allocated in the given region instead of having a new region |
| * created. */ |
| void* tsf_parser_parse_in(tsf_parser_t *parser, |
| tsf_buffer_t *buffer, |
| void *region); |
| |
| /* parse directly into an instance of the expected type using the given |
| * region. you can pass NULL for the region if you want to use malloc for |
| * all allocations. */ |
| tsf_bool_t tsf_parser_parse_into(tsf_parser_t *parser, |
| tsf_buffer_t *buffer, |
| void *result, |
| void *region); |
| |
| /* destroy a parser. */ |
| void tsf_parser_destroy(tsf_parser_t *parser); |
| |
| /* get the number of gpc codes that the parser is using. this is a |
| * measure of the parser's memory efficiency. */ |
| uint32_t tsf_parser_num_codes(tsf_parser_t *parser); |
| |
| /* create a copier given a destination type and a source type. if the |
| * source is not instanceof the destination, you will get an error. */ |
| tsf_copier_t* tsf_copier_create(tsf_type_t *dest, |
| tsf_type_t *src); |
| |
| /* destroy the copier. */ |
| void tsf_copier_destroy(tsf_copier_t *copier); |
| |
| /* clone the source data, which has the source type, and produce an |
| * object of the destination type. the object returned can be |
| * freed with tsf_region_free(). */ |
| void* tsf_copier_clone(tsf_copier_t *copier, |
| void *src); |
| |
| /* clone the source data, which has the source type, and produce an |
| * object of the destination type. the object returned will be |
| * allocated in the given region. */ |
| void* tsf_copier_clone_in(tsf_copier_t *copier, |
| void *src, |
| void *region); |
| |
| /* copy from the source to the destination, following the specified types. |
| * also, use the provided region for allocation. note that you can pass a |
| * NULL region, which causes this to use malloc for allocations. */ |
| tsf_bool_t tsf_copier_copy(tsf_copier_t *copier, |
| void *dest, |
| void *src, |
| void *region); |
| |
| /* create a destructor. a destroyer can be used to free the malloc'd |
| * memory used by objects of a given type, provided that the objects were |
| * allocated with malloc and not with a region. */ |
| tsf_destructor_t* tsf_destructor_create(tsf_type_t *type); |
| |
| /* destroy the destroyer. */ |
| void tsf_destructor_destroy(tsf_destructor_t *destructor); |
| |
| /* destruct an object. */ |
| void tsf_destructor_destruct(tsf_destructor_t *destructor, |
| void *object); |
| |
| /* file descriptor writer; the void *arg should be a pointer to |
| * an integer holding the file descriptor. */ |
| tsf_bool_t tsf_fd_writer(void *arg, |
| const void *data, |
| uint32_t len); |
| |
| /* file descriptor reader; the void *arg should be a pointer to |
| * an integer holding the file descriptor. if an EOF happens before |
| * any data is ready, this function will set the TSF_E_UNEXPECTED_EOF |
| * error. if an EOF happens after any data is ready, |
| * TSF_E_ERRONEOUS_EOF will be set instead. if the EOF was expected, |
| * you should change the TSF_E_UNEXPECTED_EOF error to TSF_E_EOF if |
| * you propagate that error (see tsf_in_man.c for a good example of |
| * what I'm talking about). */ |
| tsf_bool_t tsf_fd_reader(void *arg, |
| void *data, |
| uint32_t len); |
| |
| /* file descriptor partial reader; the void *arg should be a pointer |
| * to an integer holding the file descriptor. this differs from |
| * tsf_fd_reader() in that complete writes are never guaranteed. |
| * if an EOF happens, this function will set the TSF_E_UNEXPECTED_EOF |
| * error. if the EOF was expected, you should change the |
| * TSF_E_UNEXPECTED_EOF error to TSF_E_EOF if you propagate that error |
| * (see tsf_in_man.c for a good example of what I'm talking about). */ |
| int32_t tsf_fd_partial_reader(void *arg, |
| void *data, |
| uint32_t len); |
| |
| tsf_bool_t tsf_full_read_of_partial(tsf_partial_reader_t reader, |
| void *arg, |
| void *data, |
| uint32_t len); |
| |
| /* read a string using a reader */ |
| char* tsf_read_string(tsf_reader_t reader, |
| void *arg, |
| size_t max_size); |
| |
| /* write a string using a writer */ |
| tsf_bool_t tsf_write_string(tsf_writer_t writer, |
| void *arg, |
| const char *str); |
| |
| /* size calculating writer; the arg should be a uint32_t*, where |
| * the referenced uint32_t is the length. you should initialize |
| * the length to 0 before using this writer. note that this |
| * writer does not write the contents to anywhere; it only tells |
| * you the contents' size. */ |
| tsf_bool_t tsf_size_calc_writer(void *arg, |
| const void *buf, |
| uint32_t len); |
| |
| /* reader that delegates to another reader, but aborts once the |
| * amount read exceeds a limit. the error will be |
| * TSF_E_PARSE_ERROR. the arg should be a tsf_size_aware_rdr_t*, |
| * which you should initialize with the reader to delegate to, |
| * the arg that that reader expects, a limit, and a name. the |
| * name is used for generating the message for the parse error, |
| * if it occurs, and should be a short (preferably one word) |
| * description of what it is you're reading. */ |
| tsf_bool_t tsf_size_aware_reader(void *arg, |
| void *buf, |
| uint32_t len); |
| |
| /* buffer-only writer. writes to a buffer of a pre-defined size. you can |
| * then use this buffer with the buffer-only reader. the argument is a |
| * uint8_t**. this function will dereference it and use it as an iterator |
| * as it writes. */ |
| tsf_bool_t tsf_buffer_only_writer(void *arg, |
| const void *buf, |
| uint32_t len); |
| |
| /* buffer-only writer that resizes the output buffer when there isn't |
| * enough room. takes a tsf_resizable_buffer_t. it's valid to start by |
| * memsetting the tsf_resizable_buffer_t to zero. */ |
| tsf_bool_t tsf_resizable_buffer_writer(void *arg, |
| const void *buf, |
| uint32_t len); |
| |
| /* buffer-only reader. reads from a light-weight buffer. */ |
| tsf_bool_t tsf_buffer_only_reader(void *arg, |
| void *buf, |
| uint32_t len); |
| |
| /* reads from a reader and compares to what you write to it. fails if any |
| byte doesn't match. */ |
| tsf_bool_t tsf_comparing_writer(void *arg, |
| const void *buf, |
| uint32_t len); |
| |
| /* computes a hash of the text you give it. the arg must point to a |
| 32-bit integer. */ |
| tsf_bool_t tsf_hashing_writer(void *arg, |
| const void *buf, |
| uint32_t len); |
| |
| /* create a SHA1 hashing writer. */ |
| tsf_sha1_wtr_t* tsf_sha1_writer_create(void); |
| |
| /* destroy a SHA1 hashing writer. */ |
| void tsf_sha1_writer_destroy(tsf_sha1_wtr_t *sha1); |
| |
| /* write using the SHA1 hashing writer. */ |
| tsf_bool_t tsf_sha1_writer_write(void *arg, |
| const void *buf, |
| uint32_t len); |
| |
| /* finish writing and get the hash as an array of 5 uint32_t's. after |
| * this function is called you cannot write into this writer anymore. |
| * calling this function repeatedly will simply return the same |
| * result. */ |
| uint32_t* tsf_sha1_writer_finish(tsf_sha1_wtr_t *sha1); |
| |
| /* get the resulting hash as an array of 5 uint32_t's; this returns |
| * the same array as tsf_sha1_writer_finish(). you should not call |
| * this function until after you have called tsf_sha1_writer_finish(). |
| * in this sense, this function is somewhat redundant to |
| * tsf_sha1_writer_finish(), but is provided for convenience. */ |
| uint32_t* tsf_sha1_writer_result(tsf_sha1_wtr_t *sha1); |
| |
| /* returns a string containing the hex format SHA1 sum, given the |
| * sum in the argument. note, the returned string is newly allocated, |
| * so you must free it when you're done. */ |
| char* tsf_sha1_sum_to_str(uint32_t *sum); |
| |
| /* create a buffered reader using the given file descriptor and |
| * buffer size. if keep_fd is tsf_true, the file descriptor will be |
| * closed if: an error occurs while trying to create the buffer, |
| * and when the buffer is destroyed. */ |
| tsf_buf_rdr_t* tsf_buf_reader_create(int fd, |
| uint32_t buf_size, |
| tsf_bool_t keep_fd); |
| |
| /* destroy a read buffer */ |
| void tsf_buf_reader_destroy(tsf_buf_rdr_t *buf); |
| |
| /* read using a read buffer */ |
| tsf_bool_t tsf_buf_reader_read(void *buf, |
| void *data, |
| uint32_t len); |
| |
| /* tells you how many bytes of data are remaining in the buffer. */ |
| uint32_t tsf_buf_reader_get_remaining(tsf_buf_rdr_t *buf); |
| |
| /* resets the buffer. */ |
| void tsf_buf_reader_reset(tsf_buf_rdr_t *buf); |
| |
| /* create a buffered writer using the given file descriptor, buffer size. |
| * if keep_fd is tsf_true, the file descriptor will be closed if: an error |
| * occurs while trying to create the buffer, and when the buffer is destroyed. */ |
| tsf_buf_wtr_t* tsf_buf_writer_create(int fd, |
| uint32_t buf_size, |
| tsf_bool_t keep_fd); |
| |
| /* destroy the buffer. if keep_fd is tsf_true, the file descriptor |
| * is closed. note that this operation does not result in the |
| * buffer being flushed, so calling this without calling the |
| * flush function (see below) first may result in data loss. */ |
| void tsf_buf_writer_destroy(tsf_buf_wtr_t *buf); |
| |
| /* this is the function that you pass to all of the writing operations |
| * if you actually want to put this writer to good use. */ |
| tsf_bool_t tsf_buf_writer_write(void *buf, |
| const void *data, |
| uint32_t len); |
| |
| /* flush the buffer. */ |
| tsf_bool_t tsf_buf_writer_flush(tsf_buf_wtr_t *buf); |
| |
| /* get the 'default' zip mode. this is actually NONE, which will |
| * result in illegal argument errors in tsf_zip_reader_create() and |
| * tsf_zip_writer_create(). however, tsf_stream_file_input and |
| * tsf_stream_file_output will gracefully accept it and take it |
| * to mean that normal buffered IO should be performed. */ |
| void tsf_zip_wtr_attr_get_default(tsf_zip_wtr_attr_t *attr); |
| |
| /* get the default ZLib mode. this will cause ZLib to be used with |
| * default settings (a compression level of Z_DEFAULT_COMPRESSION and |
| * advanced_init set to false). this function will return tsf_true |
| * if ZLib is supported, and tsf_false otherwise. if tsf_false is |
| * returned, the error will be set to TSF_E_NOT_SUPPORTED. to make |
| * life easier in some cases, you can pass NULL to this function and |
| * the function will still perform the check if Zlib is supported. */ |
| tsf_bool_t tsf_zip_wtr_attr_get_zlib_default(tsf_zip_wtr_attr_t *attr); |
| |
| /* get the default libbzip2 mode. this will cause libbzip2 to be |
| * used with default settings (blockSize100k = 9, verbosity = 0, |
| * workFactor = 0, small = 0). this function will return tsf_true |
| * if libbzip2 is supported, and tsf_false otherwise. if tsf_false |
| * is returned, the error will be set to TSF_E_NOT_SUPPORTED. to |
| * make life easier in some cases, you can pass NULL to this function |
| * and the function will still perform the check if libbzip2 is |
| * supported. */ |
| tsf_bool_t tsf_zip_wtr_attr_get_bzip2_default(tsf_zip_wtr_attr_t *attr); |
| |
| /* get the attributes that correspond to the given mode. */ |
| tsf_bool_t tsf_zip_wtr_attr_get_for_mode(tsf_zip_wtr_attr_t *attr, |
| tsf_zip_mode_t mode); |
| |
| /* get the default decompression settings. this enables only those |
| * complression schemes that are actually supported. */ |
| void tsf_zip_rdr_attr_get_default(tsf_zip_rdr_attr_t *attr); |
| |
| /* get the settings with all compression disabled. this sets the |
| * allow bits to false, but all of the other fields are set to the |
| * default settings - so this is the perfect starting place if you want |
| * to enable only your approved compression schemes (just call this |
| * and then set the bits of the schemes that make you happy). */ |
| void tsf_zip_rdr_attr_get_nozip(tsf_zip_rdr_attr_t *attr); |
| |
| /* is all compression disabled? */ |
| tsf_bool_t tsf_zip_rdr_attr_is_nozip(tsf_zip_rdr_attr_t *attr); |
| |
| /* convenience macro that checks if zlib support is available */ |
| #define tsf_zlib_supported() \ |
| (tsf_zip_wtr_attr_get_zlib_default(NULL)) |
| |
| /* convenience macro that checks if libbzip2 support is available */ |
| #define tsf_libbzip2_supported() \ |
| (tsf_zip_wtr_attr_get_bzip2_default(NULL)) |
| |
| /* create a zipping reader. it also does buffering. accepts a partial |
| * reader as a delegate to actually read the data. as such, this thing |
| * can adapt to many different underlying devices. returns an error |
| * if you try to use a mode or attribute set that is not supported. |
| * In particular, using TSF_ZIP_NONE is not supported! |
| * |
| * note that this function does not check if the selected mode is allowed |
| * according to the attributes. it is the caller's responsibility to |
| * ensure that the mode is allowed. */ |
| tsf_zip_rdr_t* tsf_zip_reader_create(tsf_partial_reader_t reader, |
| void *reader_arg, |
| tsf_zip_mode_t mode, |
| const tsf_zip_rdr_attr_t *attr); |
| |
| /* destroy the reader. */ |
| void tsf_zip_reader_destroy(tsf_zip_rdr_t *reader); |
| |
| /* read some data. */ |
| tsf_bool_t tsf_zip_reader_read(void *reader, |
| void *data, |
| uint32_t len); |
| |
| /* create a zipping writer. it also does buffering. accepts a writer |
| * as a delegate to actually write the data. as such, this thing can |
| * adapt to many different underlying devices. note that it makes little |
| * or no sense to pass in a buffering writer, since this writer already |
| * does buffering (in fact, it has to do buffering to interact with the |
| * underlying compression engine). returns an error if you try to use |
| * a mode or attribute set that is not supported. in particular, |
| * TSF_ZIP_NONE is not supported! */ |
| tsf_zip_wtr_t* tsf_zip_writer_create(tsf_writer_t writer, |
| void *writer_arg, |
| const tsf_zip_wtr_attr_t *attr); |
| |
| /* destroy the writer. */ |
| void tsf_zip_writer_destroy(tsf_zip_wtr_t *writer); |
| |
| /* write some data. */ |
| tsf_bool_t tsf_zip_writer_write(void *writer, |
| const void *data, |
| uint32_t len); |
| |
| /* flush data. calling this will almost certainly hurt compression, so |
| * call it only if you really have to. if the full_flush parameter is |
| * true, the flush will not only be complete enough to allow the reading |
| * end to get all of the data, but will also result in the compressor |
| * resetting its state (so to speak) in order to make all future data |
| * be independant of the already written data. passing tsf_true for |
| * full_flush will help error recovery but will hurt your compression |
| * ratios even more. |
| * |
| * note that when using libbzip2, full_flush is ignored. in general, |
| * any flushing done with libbzip2 will hurt a lot more than any flushing |
| * done with zlib, regardless of the value of full_flush. so keep that |
| * in mind. in many cases, the extremely bad performance that results |
| * from flushing in libbzip2 will be sufficient reason to use zlib |
| * instead. */ |
| tsf_bool_t tsf_zip_writer_flush(tsf_zip_wtr_t *writer, |
| tsf_bool_t full_flush); |
| |
| /* finish the stream. this allows the compressor to output its end-of- |
| * stream marker. this also does an implicit flush, so calling flush |
| * just before calling finish will not help (it shouldn't hurt, either). |
| * not that if you do a flush but do not do a finish, the receiver |
| * should be able to get all of the data but will probably report an |
| * error at the end of the file, complaining that the end of stream was |
| * never really hit. |
| * |
| * after finish returns, you may not use this writer anymore. |
| * attempting to call writer, flush, or finish on a writer that has |
| * already had finish called on it will lead to unpredictable behavior. |
| * after calling finish, you should just call destroy. */ |
| tsf_bool_t tsf_zip_writer_finish(tsf_zip_wtr_t *writer); |
| |
| /* create an adaptive zipping reader. this reader will be able to switch |
| * between different zipping strategies (including no zipping) as the input |
| * data indicates. however, it only works under two non-trivial |
| * conditions: |
| * 1) the input is formatted according to the serial protocol with |
| * stream file headers indicating compression settings, and |
| * 2) you call tsf_adaptive_reader_hint_switch() at the right times. */ |
| tsf_adpt_rdr_t* tsf_adaptive_reader_create(tsf_partial_reader_t reader, |
| void *reader_arg, |
| uint32_t nozip_buf_size, |
| const tsf_zip_rdr_attr_t *attr); |
| |
| /* close the adaptive reader. cleans up everything. never fails. */ |
| void tsf_adaptive_reader_destroy(tsf_adpt_rdr_t *reader); |
| |
| /* inform the reader that there may be a format change in the next byte. |
| * if we are currently reading with compression turned off, this hint tells |
| * the reader to inspect the next four bytes to see if it contains a code |
| * corresponding to the start of a ZLIB or BZIP2 sequence. if we are using |
| * a ZLIB or BZIP2 reader, this specifies that an end-of-stream from the |
| * underlying reader is correct (otherwise it is flagged as erroneous). */ |
| void tsf_adaptive_reader_hint_switch(tsf_adpt_rdr_t *reader); |
| |
| /* tells you how many bytes of data are remaining in the buffer. */ |
| uint32_t tsf_adaptive_reader_get_remaining(tsf_adpt_rdr_t *reader); |
| |
| /* the actual read method. */ |
| tsf_bool_t tsf_adaptive_reader_read(void *reader, |
| void *data, |
| uint32_t len); |
| |
| /* determine the zip mode of the given file by openning it and reading the |
| * first four bytes. if the mode returned is TSF_ZIP_UNKNOWN then there |
| * was an error. if the error was TSF_E_NOT_SUPPORTED then the file is |
| * not in a valid TSF stream file format. */ |
| tsf_zip_mode_t tsf_stream_file_get_mode(const char *filename); |
| |
| /* same as above except that it reads the first four bytes from the given |
| * file descriptor. */ |
| tsf_zip_mode_t tsf_stream_file_get_mode_from_fd(int fd); |
| |
| /* open a file for reading. if the zip reader attributes are NULL then they |
| * are set to the defaults. if the limits are NULL then no limits are used. |
| * instances of this type are not thread-safe; use locking around calls into |
| * the object. */ |
| tsf_stream_file_input_t* |
| tsf_stream_file_input_open(const char *filename, |
| tsf_limits_t *limits, |
| tsf_zip_rdr_attr_t *attr); |
| |
| /* make a stream file input hijack a file descriptor. if the zip reader |
| * attributes are NULL then they are set to the defaults. |
| * |
| * if keep is true, the file descriptor will be closed when |
| * tsf_stream_file_input_close() is called. otherwise, the file |
| * descriptor will remain open. */ |
| tsf_stream_file_input_t* |
| tsf_stream_file_input_fd_open(int fd, |
| tsf_limits_t *limits, |
| tsf_zip_rdr_attr_t *attr, |
| tsf_bool_t keep, |
| uint32_t buf_size); |
| |
| /* close a file that was openned for reading. */ |
| void tsf_stream_file_input_close(tsf_stream_file_input_t *file); |
| |
| /* returns true if the file allows compression. compressed files have some |
| * limitations. for example, mark & seek is not supported for compressed |
| * files. */ |
| tsf_bool_t tsf_stream_file_input_allows_compression(tsf_stream_file_input_t *file); |
| |
| /* register a callback that will get called every time the input manager |
| * encounters a new type. */ |
| tsf_bool_t tsf_stream_file_input_register_type_cback(tsf_stream_file_input_t *file, |
| tsf_type_cback_t cback, |
| void *arg); |
| |
| /* unregister all type callbacks that match the given function pointer |
| * and void* argument. */ |
| tsf_bool_t tsf_stream_file_input_unregister_type_cback(tsf_stream_file_input_t *file, |
| tsf_type_cback_t cback, |
| void *arg); |
| |
| /* read a buffer from a file that was openned for reading. */ |
| tsf_buffer_t* tsf_stream_file_input_read(tsf_stream_file_input_t *file); |
| |
| /* get the mark of the thing you are about to read. the mark is something |
| * that you get to allocate. but you must treat it as an opaque! |
| * |
| * note that mark & seek is not supported for files that are compressed. |
| * it is also not supported if the underlying file descriptor does not |
| * support lseek(). if the file is compressed, you will get a |
| * TSF_E_NOT_SUPPORTED error. it is perfectly safe (and fast) to determine |
| * if a file is compressed by calling this function. you can of course |
| * also use tsf_stream_file_input_is_compressed(). if the file does not |
| * support lseek(), you will get some sort of system error, which may |
| * differ depending on how well your system complies to POSIX. :-) */ |
| tsf_bool_t tsf_stream_file_input_get_mark(tsf_stream_file_input_t *file, |
| tsf_stream_file_mark_t *mark); |
| |
| /* seek to previously acquired mark. */ |
| tsf_bool_t tsf_stream_file_input_seek(tsf_stream_file_input_t *file, |
| const tsf_stream_file_mark_t *mark); |
| |
| /* open a file for writing. if the zip attributes are NULL, the default |
| * ones will be used (which means no compression). instances of this type |
| * are not thread-safe; use locking when calling its methods. */ |
| tsf_stream_file_output_t* |
| tsf_stream_file_output_open(const char *filename, |
| tsf_bool_t append, |
| tsf_zip_wtr_attr_t *attr); |
| |
| /* make a stream file output hijack a file descriptor. if keep is true, the |
| * file descriptor will be closed when tsf_stream_file_output_close() is |
| * called. otherwise, it will remain open. */ |
| tsf_stream_file_output_t* |
| tsf_stream_file_output_fd_open(int fd, |
| tsf_zip_wtr_attr_t *attr, |
| tsf_bool_t keep, |
| uint32_t buf_size); |
| |
| /* flush and close a stream file output. returns tsf_true if the flushing |
| * went OK and tsf_false if it didn't. the file gets closed either way, |
| * though. */ |
| tsf_bool_t tsf_stream_file_output_close(tsf_stream_file_output_t *file); |
| |
| /* flush the stream file output. if this fails, the stream becomes corrupt |
| * at the point of failure and should not be used. */ |
| tsf_bool_t tsf_stream_file_output_flush(tsf_stream_file_output_t *file); |
| |
| /* write something into the file. if this fails, the stream becomes corrupt |
| * at the point of failure and should not be used. */ |
| tsf_bool_t tsf_stream_file_output_write(tsf_stream_file_output_t *file, |
| tsf_buffer_t *buffer); |
| |
| /* open a filesystem database. a filesystem database allows for key-value |
| * storage optimized for storing huge values. additionally, a filesystem |
| * database allows a value to consist of a sequence of buffers (i.e. a |
| * stream), and it has support for connecting to a remote database. |
| * |
| * one of the main features of FSDB is its excellent concurrency support. |
| * even if an FSDB is opened by multiple different processes, or multiple |
| * threads in a single process, or multiple threads share the same |
| * tsf_fsdb_t* handle, the following properties are guaranteed: |
| * |
| * - tsf_fsdb_put() is not visible unless there is a subsequent |
| * tsf_fsdb_out_commit(). |
| * |
| * - if multiple tsf_fsdb_out_commit()'s are run using the same key, |
| * then any tsf_fsdb_get()'s behave as if the only tsf_fsdb_put() |
| * and tsf_fsdb_out_commit() that had ever been executed was the last |
| * one at the time of the tsf_fsdb_get(). |
| * |
| * - tsf_fsdb_get() returns a stream that does not change even if |
| * new tsf_fsdb_put()/tsf_fsdb_out_write()/tsf_fsdb_out_commit() are |
| * executed on the same key. |
| * |
| * - tsf_fsdb_out_abort() makes it appear as if the tsf_fsdb_put() and |
| * any subsequent tsf_fsdb_out_write()'s never happened. |
| * |
| * - if tsf_fsdb_out_commit() fails it is no different than if you had |
| * called tsf_fsdb_out_abort(). |
| * |
| * These properties are guaranteed even when accessing the database |
| * using different tsf_fsdb_t* handles (even from separate processes), |
| * so long as the underlying filesystem supports the atomicity |
| * guarantees of open(O_EXCL) and rename(). |
| * |
| * The memory management is such that the FSDB API never claims |
| * ownership of any objects, but the tsf_fsdb_t* must be kept alive so |
| * long as there are tsf_fsdb_in_t* or tsf_fsdb_out_t* that have been |
| * created from it. */ |
| tsf_fsdb_t* tsf_fsdb_open_local(const char *dirname, |
| tsf_limits_t *limits, |
| tsf_zip_rdr_attr_t *zip_rdr_attr, |
| tsf_zip_wtr_attr_t *zip_wtr_attr, |
| tsf_bool_t update, |
| tsf_bool_t truncate); |
| |
| /* open a remote FSDB database using a host and port. */ |
| tsf_fsdb_t* tsf_fsdb_open_remote(const char *host, |
| int port, |
| tsf_limits_t *limits, |
| tsf_zip_rdr_attr_t *zip_rdr_attr, |
| tsf_zip_wtr_attr_t *zip_wtr_attr); |
| |
| /* close a filesystem database. */ |
| void tsf_fsdb_close(tsf_fsdb_t *fsdb); |
| |
| /* open a stream to write a sequence of values at a key in the database. */ |
| tsf_fsdb_out_t* tsf_fsdb_put(tsf_fsdb_t *fsdb, |
| tsf_buffer_t *key); |
| |
| /* open a stream to read a sequence of values at a key in the database. */ |
| tsf_fsdb_in_t* tsf_fsdb_get(tsf_fsdb_t *fsdb, |
| tsf_buffer_t *key); |
| |
| /* delete an entry from the database. */ |
| tsf_bool_t tsf_fsdb_rm(tsf_fsdb_t *fsdb, |
| tsf_buffer_t *key); |
| |
| /* close a filesystem database input stream. */ |
| void tsf_fsdb_in_close(tsf_fsdb_in_t *in); |
| |
| /* read a buffer from a filesystem database input stream. */ |
| tsf_buffer_t* tsf_fsdb_in_read(tsf_fsdb_in_t *in); |
| |
| /* commit a filesystem database output stream, and destroy the stream. |
| * if this experiences an error, then it means that the stream is |
| * automatically aborted. */ |
| tsf_bool_t tsf_fsdb_out_commit(tsf_fsdb_out_t *out); |
| |
| /* abort a filesystem database output stream, and destroy the stream. */ |
| void tsf_fsdb_out_abort(tsf_fsdb_out_t *out); |
| |
| /* write to a filesystem database output stream. */ |
| tsf_bool_t tsf_fsdb_out_write(tsf_fsdb_out_t *out, |
| tsf_buffer_t *buf); |
| |
| #ifdef __cplusplus |
| }; |
| #endif |
| |
| #endif |
| |