#include "prism/pack.h" // We optionally support parsing String#pack templates. For systems that don't // want or need this functionality, it can be turned off with the // PRISM_EXCLUDE_PACK define. #ifdef PRISM_EXCLUDE_PACK void pm_pack_parse(void) {} #else #include #include 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; } PRISM_EXPORTED_FUNCTION pm_pack_result pm_pack_parse( pm_pack_variant variant, const char **format, const char *format_end, pm_pack_type *type, pm_pack_signed *signed_type, pm_pack_endian *endian, pm_pack_size *size, pm_pack_length_type *length_type, uint64_t *length, pm_pack_encoding *encoding ) { if (*encoding == PM_PACK_ENCODING_START) { *encoding = PM_PACK_ENCODING_US_ASCII; } if (*format == format_end) { *type = PM_PACK_END; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; *length_type = PM_PACK_LENGTH_NA; return PM_PACK_OK; } *length_type = PM_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 = PM_PACK_SPACE; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; *length_type = PM_PACK_LENGTH_NA; *length = 0; return PM_PACK_OK; case '#': while ((*format < format_end) && (**format != '\n')) { (*format)++; } *type = PM_PACK_COMMENT; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; *length_type = PM_PACK_LENGTH_NA; *length = 0; return PM_PACK_OK; case 'C': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_AGNOSTIC_ENDIAN; *size = PM_PACK_SIZE_8; break; case 'S': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_16; break; case 'L': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_32; break; case 'Q': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_64; break; case 'J': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_P; break; case 'c': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_SIGNED; *endian = PM_PACK_AGNOSTIC_ENDIAN; *size = PM_PACK_SIZE_8; break; case 's': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_SIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_16; break; case 'l': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_SIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_32; break; case 'q': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_SIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_64; break; case 'j': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_SIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_P; break; case 'I': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_INT; break; case 'i': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_SIGNED; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_INT; break; case 'n': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_BIG_ENDIAN; *size = PM_PACK_SIZE_16; length_changed_allowed = false; break; case 'N': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_BIG_ENDIAN; *size = PM_PACK_SIZE_32; length_changed_allowed = false; break; case 'v': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_LITTLE_ENDIAN; *size = PM_PACK_SIZE_16; length_changed_allowed = false; break; case 'V': *type = PM_PACK_INTEGER; *signed_type = PM_PACK_UNSIGNED; *endian = PM_PACK_LITTLE_ENDIAN; *size = PM_PACK_SIZE_32; length_changed_allowed = false; break; case 'U': *type = PM_PACK_UTF8; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'w': *type = PM_PACK_BER; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'D': case 'd': *type = PM_PACK_FLOAT; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_64; break; case 'F': case 'f': *type = PM_PACK_FLOAT; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_NATIVE_ENDIAN; *size = PM_PACK_SIZE_32; break; case 'E': *type = PM_PACK_FLOAT; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_LITTLE_ENDIAN; *size = PM_PACK_SIZE_64; break; case 'e': *type = PM_PACK_FLOAT; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_LITTLE_ENDIAN; *size = PM_PACK_SIZE_32; break; case 'G': *type = PM_PACK_FLOAT; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_BIG_ENDIAN; *size = PM_PACK_SIZE_64; break; case 'g': *type = PM_PACK_FLOAT; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_BIG_ENDIAN; *size = PM_PACK_SIZE_32; break; case 'A': *type = PM_PACK_STRING_SPACE_PADDED; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'a': *type = PM_PACK_STRING_NULL_PADDED; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'Z': *type = PM_PACK_STRING_NULL_TERMINATED; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'B': *type = PM_PACK_STRING_MSB; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'b': *type = PM_PACK_STRING_LSB; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'H': *type = PM_PACK_STRING_HEX_HIGH; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'h': *type = PM_PACK_STRING_HEX_LOW; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'u': *type = PM_PACK_STRING_UU; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'M': *type = PM_PACK_STRING_MIME; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'm': *type = PM_PACK_STRING_BASE64; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'P': *type = PM_PACK_STRING_FIXED; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'p': *type = PM_PACK_STRING_POINTER; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case '@': *type = PM_PACK_MOVE; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'X': *type = PM_PACK_BACK; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case 'x': *type = PM_PACK_NULL; *signed_type = PM_PACK_SIGNED_NA; *endian = PM_PACK_ENDIAN_NA; *size = PM_PACK_SIZE_NA; break; case '%': return PM_PACK_ERROR_UNSUPPORTED_DIRECTIVE; default: return PM_PACK_ERROR_UNKNOWN_DIRECTIVE; } bool explicit_endian = false; while (*format < format_end) { switch (**format) { case '_': case '!': (*format)++; if (*type != PM_PACK_INTEGER || !length_changed_allowed) { return PM_PACK_ERROR_BANG_NOT_ALLOWED; } switch (*size) { case PM_PACK_SIZE_SHORT: case PM_PACK_SIZE_INT: case PM_PACK_SIZE_LONG: case PM_PACK_SIZE_LONG_LONG: break; case PM_PACK_SIZE_16: *size = PM_PACK_SIZE_SHORT; break; case PM_PACK_SIZE_32: *size = PM_PACK_SIZE_LONG; break; case PM_PACK_SIZE_64: *size = PM_PACK_SIZE_LONG_LONG; break; case PM_PACK_SIZE_P: break; default: return PM_PACK_ERROR_BANG_NOT_ALLOWED; } break; case '<': (*format)++; if (explicit_endian) { return PM_PACK_ERROR_DOUBLE_ENDIAN; } *endian = PM_PACK_LITTLE_ENDIAN; explicit_endian = true; break; case '>': (*format)++; if (explicit_endian) { return PM_PACK_ERROR_DOUBLE_ENDIAN; } *endian = PM_PACK_BIG_ENDIAN; explicit_endian = true; break; default: goto exit_modifier_loop; } } exit_modifier_loop: if (variant == PM_PACK_VARIANT_UNPACK && *type == PM_PACK_MOVE) { *length = 0; } if (*format < format_end) { if (**format == '*') { switch (*type) { case PM_PACK_NULL: case PM_PACK_BACK: switch (variant) { case PM_PACK_VARIANT_PACK: *length_type = PM_PACK_LENGTH_FIXED; break; case PM_PACK_VARIANT_UNPACK: *length_type = PM_PACK_LENGTH_MAX; break; } *length = 0; break; case PM_PACK_MOVE: switch (variant) { case PM_PACK_VARIANT_PACK: *length_type = PM_PACK_LENGTH_FIXED; break; case PM_PACK_VARIANT_UNPACK: *length_type = PM_PACK_LENGTH_RELATIVE; break; } *length = 0; break; case PM_PACK_STRING_UU: *length_type = PM_PACK_LENGTH_FIXED; *length = 0; break; case PM_PACK_STRING_FIXED: switch (variant) { case PM_PACK_VARIANT_PACK: *length_type = PM_PACK_LENGTH_FIXED; *length = 1; break; case PM_PACK_VARIANT_UNPACK: *length_type = PM_PACK_LENGTH_MAX; *length = 0; break; } break; case PM_PACK_STRING_MIME: case PM_PACK_STRING_BASE64: *length_type = PM_PACK_LENGTH_FIXED; *length = 1; break; default: *length_type = PM_PACK_LENGTH_MAX; *length = 0; break; } (*format)++; } else if (**format >= '0' && **format <= '9') { errno = 0; *length_type = PM_PACK_LENGTH_FIXED; #if UINTMAX_MAX < UINT64_MAX #error "prism'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 PM_PACK_ERROR_LENGTH_TOO_BIG; } *length = (uint64_t) length_max; } } switch (*type) { case PM_PACK_UTF8: /* if encoding is US-ASCII, upgrade to UTF-8 */ if (*encoding == PM_PACK_ENCODING_US_ASCII) { *encoding = PM_PACK_ENCODING_UTF_8; } break; case PM_PACK_STRING_MIME: case PM_PACK_STRING_BASE64: case PM_PACK_STRING_UU: /* keep US-ASCII (do nothing) */ break; default: /* fall back to BINARY */ *encoding = PM_PACK_ENCODING_ASCII_8BIT; break; } return PM_PACK_OK; } PRISM_EXPORTED_FUNCTION size_t pm_size_to_native(pm_pack_size size) { switch (size) { case PM_PACK_SIZE_SHORT: return sizeof(short); case PM_PACK_SIZE_INT: return sizeof(int); case PM_PACK_SIZE_LONG: return sizeof(long); case PM_PACK_SIZE_LONG_LONG: return sizeof(long long); case PM_PACK_SIZE_8: return 1; case PM_PACK_SIZE_16: return 2; case PM_PACK_SIZE_32: return 4; case PM_PACK_SIZE_64: return 8; case PM_PACK_SIZE_P: return sizeof(void *); default: return 0; } } #endif