diff options
Diffstat (limited to 'prism/pack.c')
-rw-r--r-- | prism/pack.c | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/prism/pack.c b/prism/pack.c new file mode 100644 index 0000000000..48bba4ea49 --- /dev/null +++ b/prism/pack.c @@ -0,0 +1,493 @@ +#include "yarp/pack.h" + +#include <stdbool.h> +#include <errno.h> + +static uintmax_t +strtoumaxc(const char **format); + +YP_EXPORTED_FUNCTION yp_pack_result +yp_pack_parse(yp_pack_variant variant, const char **format, const char *format_end, + yp_pack_type *type, yp_pack_signed *signed_type, yp_pack_endian *endian, yp_pack_size *size, + yp_pack_length_type *length_type, uint64_t *length, yp_pack_encoding *encoding) { + + if (*encoding == YP_PACK_ENCODING_START) { + *encoding = YP_PACK_ENCODING_US_ASCII; + } + + if (*format == format_end) { + *type = YP_PACK_END; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + *length_type = YP_PACK_LENGTH_NA; + return YP_PACK_OK; + } + + *length_type = YP_PACK_LENGTH_FIXED; + *length = 1; + bool length_changed_allowed = true; + + char directive = **format; + (*format)++; + switch (directive) { + case ' ': + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + *type = YP_PACK_SPACE; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + *length_type = YP_PACK_LENGTH_NA; + *length = 0; + return YP_PACK_OK; + case '#': + while ((*format < format_end) && (**format != '\n')) { + (*format)++; + } + *type = YP_PACK_COMMENT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + *length_type = YP_PACK_LENGTH_NA; + *length = 0; + return YP_PACK_OK; + case 'C': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_AGNOSTIC_ENDIAN; + *size = YP_PACK_SIZE_8; + break; + case 'S': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_16; + break; + case 'L': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'Q': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'J': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_P; + break; + case 'c': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_AGNOSTIC_ENDIAN; + *size = YP_PACK_SIZE_8; + break; + case 's': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_16; + break; + case 'l': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'q': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'j': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_P; + break; + case 'I': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_INT; + break; + case 'i': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_INT; + break; + case 'n': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_BIG_ENDIAN; + *size = YP_PACK_SIZE_16; + length_changed_allowed = false; + break; + case 'N': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_BIG_ENDIAN; + *size = YP_PACK_SIZE_32; + length_changed_allowed = false; + break; + case 'v': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_LITTLE_ENDIAN; + *size = YP_PACK_SIZE_16; + length_changed_allowed = false; + break; + case 'V': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_LITTLE_ENDIAN; + *size = YP_PACK_SIZE_32; + length_changed_allowed = false; + break; + case 'U': + *type = YP_PACK_UTF8; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'w': + *type = YP_PACK_BER; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'D': + case 'd': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'F': + case 'f': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'E': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_LITTLE_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'e': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_LITTLE_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'G': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_BIG_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'g': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_BIG_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'A': + *type = YP_PACK_STRING_SPACE_PADDED; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'a': + *type = YP_PACK_STRING_NULL_PADDED; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'Z': + *type = YP_PACK_STRING_NULL_TERMINATED; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'B': + *type = YP_PACK_STRING_MSB; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'b': + *type = YP_PACK_STRING_LSB; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'H': + *type = YP_PACK_STRING_HEX_HIGH; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'h': + *type = YP_PACK_STRING_HEX_LOW; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'u': + *type = YP_PACK_STRING_UU; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'M': + *type = YP_PACK_STRING_MIME; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'm': + *type = YP_PACK_STRING_BASE64; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'P': + *type = YP_PACK_STRING_FIXED; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'p': + *type = YP_PACK_STRING_POINTER; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case '@': + *type = YP_PACK_MOVE; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'X': + *type = YP_PACK_BACK; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'x': + *type = YP_PACK_NULL; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case '%': + return YP_PACK_ERROR_UNSUPPORTED_DIRECTIVE; + default: + return YP_PACK_ERROR_UNKNOWN_DIRECTIVE; + } + + bool explicit_endian = false; + + while (*format < format_end) { + switch (**format) { + case '_': + case '!': + (*format)++; + if (*type != YP_PACK_INTEGER || !length_changed_allowed) { + return YP_PACK_ERROR_BANG_NOT_ALLOWED; + } + switch (*size) { + case YP_PACK_SIZE_SHORT: + case YP_PACK_SIZE_INT: + case YP_PACK_SIZE_LONG: + case YP_PACK_SIZE_LONG_LONG: + break; + case YP_PACK_SIZE_16: + *size = YP_PACK_SIZE_SHORT; + break; + case YP_PACK_SIZE_32: + *size = YP_PACK_SIZE_LONG; + break; + case YP_PACK_SIZE_64: + *size = YP_PACK_SIZE_LONG_LONG; + break; + case YP_PACK_SIZE_P: + break; + default: + return YP_PACK_ERROR_BANG_NOT_ALLOWED; + } + break; + case '<': + (*format)++; + if (explicit_endian) { + return YP_PACK_ERROR_DOUBLE_ENDIAN; + } + *endian = YP_PACK_LITTLE_ENDIAN; + explicit_endian = true; + break; + case '>': + (*format)++; + if (explicit_endian) { + return YP_PACK_ERROR_DOUBLE_ENDIAN; + } + *endian = YP_PACK_BIG_ENDIAN; + explicit_endian = true; + break; + default: + goto exit_modifier_loop; + } + } + +exit_modifier_loop: + + if (variant == YP_PACK_VARIANT_UNPACK && *type == YP_PACK_MOVE) { + *length = 0; + } + + if (*format < format_end) { + if (**format == '*') { + switch (*type) { + case YP_PACK_NULL: + case YP_PACK_BACK: + switch (variant) { + case YP_PACK_VARIANT_PACK: + *length_type = YP_PACK_LENGTH_FIXED; + break; + case YP_PACK_VARIANT_UNPACK: + *length_type = YP_PACK_LENGTH_MAX; + break; + } + *length = 0; + break; + + case YP_PACK_MOVE: + switch (variant) { + case YP_PACK_VARIANT_PACK: + *length_type = YP_PACK_LENGTH_FIXED; + break; + case YP_PACK_VARIANT_UNPACK: + *length_type = YP_PACK_LENGTH_RELATIVE; + break; + } + *length = 0; + break; + + case YP_PACK_STRING_UU: + *length_type = YP_PACK_LENGTH_FIXED; + *length = 0; + break; + + case YP_PACK_STRING_FIXED: + switch (variant) { + case YP_PACK_VARIANT_PACK: + *length_type = YP_PACK_LENGTH_FIXED; + *length = 1; + break; + case YP_PACK_VARIANT_UNPACK: + *length_type = YP_PACK_LENGTH_MAX; + *length = 0; + break; + } + break; + + case YP_PACK_STRING_MIME: + case YP_PACK_STRING_BASE64: + *length_type = YP_PACK_LENGTH_FIXED; + *length = 1; + break; + + default: + *length_type = YP_PACK_LENGTH_MAX; + *length = 0; + break; + } + + (*format)++; + } else if (**format >= '0' && **format <= '9') { + errno = 0; + *length_type = YP_PACK_LENGTH_FIXED; + #if UINTMAX_MAX < UINT64_MAX + #error "YARP's design assumes uintmax_t is at least as large as uint64_t" + #endif + uintmax_t length_max = strtoumaxc(format); + if (errno || length_max > UINT64_MAX) { + return YP_PACK_ERROR_LENGTH_TOO_BIG; + } + *length = (uint64_t) length_max; + } + } + + switch (*type) { + case YP_PACK_UTF8: + /* if encoding is US-ASCII, upgrade to UTF-8 */ + if (*encoding == YP_PACK_ENCODING_US_ASCII) { + *encoding = YP_PACK_ENCODING_UTF_8; + } + break; + case YP_PACK_STRING_MIME: + case YP_PACK_STRING_BASE64: + case YP_PACK_STRING_UU: + /* keep US-ASCII (do nothing) */ + break; + default: + /* fall back to BINARY */ + *encoding = YP_PACK_ENCODING_ASCII_8BIT; + break; + } + + return YP_PACK_OK; +} + +YP_EXPORTED_FUNCTION size_t +yp_size_to_native(yp_pack_size size) { + switch (size) { + case YP_PACK_SIZE_SHORT: + return sizeof(short); + case YP_PACK_SIZE_INT: + return sizeof(int); + case YP_PACK_SIZE_LONG: + return sizeof(long); + case YP_PACK_SIZE_LONG_LONG: + return sizeof(long long); + case YP_PACK_SIZE_8: + return 1; + case YP_PACK_SIZE_16: + return 2; + case YP_PACK_SIZE_32: + return 4; + case YP_PACK_SIZE_64: + return 8; + case YP_PACK_SIZE_P: + return sizeof(void *); + default: + return 0; + } +} + +static uintmax_t +strtoumaxc(const char **format) { + uintmax_t value = 0; + while (**format >= '0' && **format <= '9') { + if (value > UINTMAX_MAX / 10) { + errno = ERANGE; + } + value = value * 10 + ((uintmax_t) (**format - '0')); + (*format)++; + } + return value; +} |