blob: a39b78bace5b8decf29838c59501cd714a15b7fe [file] [log] [blame]
/*
* 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