summaryrefslogtreecommitdiff
path: root/prism/pack.c
diff options
context:
space:
mode:
Diffstat (limited to 'prism/pack.c')
-rw-r--r--prism/pack.c493
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;
+}