Source/PLCrashLogWriterEncoding.c (245 lines of code) (raw):

/* * Copyright 2008, Dave Benson. * Copyright 2008 - 2009 Plausible Labs Cooperative, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License * at http://www.apache.org/licenses/LICENSE-2.0 Unless * required by applicable law or agreed to in writing, * software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Extracted from protobuf-c and modified to support zero-allocation, * async-safe file encoding. * * -landonf dec 12, 2008 */ #include <stdint.h> #include <string.h> #include <stdlib.h> #include "PLCrashLogWriterEncoding.h" #define MAX_UINT64_ENCODED_SIZE 10 /* --- wire format enums --- */ typedef enum { PLPROTOBUF_C_WIRE_TYPE_VARINT, PLPROTOBUF_C_WIRE_TYPE_64BIT, PLPROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED, PLPROTOBUF_C_WIRE_TYPE_START_GROUP, /* unsupported */ PLPROTOBUF_C_WIRE_TYPE_END_GROUP, /* unsupported */ PLPROTOBUF_C_WIRE_TYPE_32BIT } PLProtobufCWireType; /* === pack() === */ static inline uint32_t zigzag32 (int32_t v) { if (v < 0) return ((uint32_t)(-v)) * 2 - 1; else return v * 2; } static inline uint64_t zigzag64 (int64_t v) { if (v < 0) return ((uint64_t)(-v)) * 2 - 1; else return v * 2; } static inline size_t uint32_pack (uint32_t value, uint8_t *out) { unsigned rv = 0; if (value >= 0x80) { out[rv++] = value | 0x80; value >>= 7; if (value >= 0x80) { out[rv++] = value | 0x80; value >>= 7; if (value >= 0x80) { out[rv++] = value | 0x80; value >>= 7; if (value >= 0x80) { out[rv++] = value | 0x80; value >>= 7; } } } } /* assert: value<128 */ out[rv++] = value; return rv; } static inline size_t int32_pack (int32_t value, uint8_t *out) { if (value < 0) { out[0] = value | 0x80; out[1] = (value>>7) | 0x80; out[2] = (value>>14) | 0x80; out[3] = (value>>21) | 0x80; out[4] = (value>>28) | 0x80; out[5] = out[6] = out[7] = out[8] = 0xff; out[9] = 0x01; return 10; } else return uint32_pack (value, out); } static inline size_t sint32_pack (int32_t value, uint8_t *out) { return uint32_pack (zigzag32 (value), out); } static size_t uint64_pack (uint64_t value, uint8_t *out) { uint32_t hi = value>>32; uint32_t lo = (uint32_t) value; unsigned rv; if (hi == 0) return uint32_pack (lo, out); out[0] = (lo) | 0x80; out[1] = (lo>>7) | 0x80; out[2] = (lo>>14) | 0x80; out[3] = (lo>>21) | 0x80; if (hi < 8) { out[4] = (hi<<4) | (lo>>28); return 5; } else { out[4] = ((hi&7)<<4) | (lo>>28) | 0x80; hi >>= 3; } rv = 5; while (hi >= 128) { out[rv++] = hi | 0x80; hi >>= 7; } out[rv++] = hi; return rv; } static inline size_t sint64_pack (int64_t value, uint8_t *out) { return uint64_pack (zigzag64 (value), out); } static inline size_t fixed32_pack (uint32_t value, uint8_t *out) { #if __LITTLE_ENDIAN__ plcrash_async_memcpy (out, &value, 4); #else out[0] = value; out[1] = value>>8; out[2] = value>>16; out[3] = value>>24; #endif return 4; } static inline size_t fixed64_pack (uint64_t value, uint8_t *out) { #if __LITTLE_ENDIAN__ plcrash_async_memcpy (out, &value, 8); #else fixed32_pack (value, out); fixed32_pack (value>>32, out+4); #endif return 8; } static inline size_t boolean_pack (bool value, uint8_t *out) { *out = value ? 1 : 0; return 1; } /* wire-type will be added in required_field_pack() */ static size_t tag_pack (uint32_t id, uint8_t *out) { if (id < (1<<(32-3))) return uint32_pack (id<<3, out); else return uint64_pack (((uint64_t)id) << 3, out); } /* === pack_to_buffer() === */ // file argument may be NULL size_t plcrash_writer_pack (plcrash_async_file_t *file, uint32_t field_id, PLProtobufCType field_type, const void *value) { size_t rv; uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2]; rv = tag_pack (field_id, scratch); switch (field_type) { case PLPROTOBUF_C_TYPE_SINT32: scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_VARINT; rv += sint32_pack (*(const int32_t *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; case PLPROTOBUF_C_TYPE_INT32: scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_VARINT; rv += int32_pack (*(const uint32_t *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; case PLPROTOBUF_C_TYPE_UINT32: case PLPROTOBUF_C_TYPE_ENUM: scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_VARINT; rv += uint32_pack (*(const uint32_t *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; case PLPROTOBUF_C_TYPE_SINT64: scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_VARINT; rv += sint64_pack (*(const int64_t *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; case PLPROTOBUF_C_TYPE_INT64: case PLPROTOBUF_C_TYPE_UINT64: scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_VARINT; rv += uint64_pack (*(const uint64_t *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; case PLPROTOBUF_C_TYPE_SFIXED32: case PLPROTOBUF_C_TYPE_FIXED32: case PLPROTOBUF_C_TYPE_FLOAT: scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_32BIT; rv += fixed32_pack (*(const uint32_t *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; case PLPROTOBUF_C_TYPE_SFIXED64: case PLPROTOBUF_C_TYPE_FIXED64: case PLPROTOBUF_C_TYPE_DOUBLE: scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_64BIT; rv += fixed64_pack (*(const uint64_t *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; case PLPROTOBUF_C_TYPE_BOOL: scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_VARINT; rv += boolean_pack (*(const bool *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; case PLPROTOBUF_C_TYPE_STRING: { uint32_t sublen = (uint32_t) strlen (value); scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; rv += uint32_pack (sublen, scratch + rv); if (file != NULL) { plcrash_async_file_write(file, scratch, rv); plcrash_async_file_write(file, value, sublen); } rv += sublen; break; } case PLPROTOBUF_C_TYPE_BYTES: { const PLProtobufCBinaryData * bd = ((const PLProtobufCBinaryData*) value); uint32_t sublen = (uint32_t) bd->len; scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; rv += uint32_pack (sublen, scratch + rv); if (file != NULL) { plcrash_async_file_write(file, scratch, rv); plcrash_async_file_write(file, bd->data, sublen); } rv += sublen; break; } //PLPROTOBUF_C_TYPE_GROUP, // NOT SUPPORTED case PLPROTOBUF_C_TYPE_MESSAGE: { scratch[0] |= PLPROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; rv += uint32_pack (*(const uint32_t *) value, scratch + rv); if (file != NULL) plcrash_async_file_write(file, scratch, rv); break; } default: PLCF_DEBUG("Unhandled field type %d", field_type); abort(); } return rv; }