1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef UPB_WIRE_READER_H_
#define UPB_WIRE_READER_H_
#include "upb/wire/eps_copy_input_stream.h"
#include "upb/wire/internal/swap.h"
#include "upb/wire/types.h" // IWYU pragma: export
// Must be last.
#include "upb/port/def.inc"
#ifdef __cplusplus
extern "C" {
#endif
// The upb_WireReader interface is suitable for general-purpose parsing of
// protobuf binary wire format. It is designed to be used along with
// upb_EpsCopyInputStream for buffering, and all parsing routines in this file
// assume that at least kUpb_EpsCopyInputStream_SlopBytes worth of data is
// available to read without any bounds checks.
#define kUpb_WireReader_WireTypeMask 7
#define kUpb_WireReader_WireTypeBits 3
typedef struct {
const char* ptr;
uint64_t val;
} _upb_WireReader_ReadLongVarintRet;
_upb_WireReader_ReadLongVarintRet _upb_WireReader_ReadLongVarint(
const char* ptr, uint64_t val);
static UPB_FORCEINLINE const char* _upb_WireReader_ReadVarint(const char* ptr,
uint64_t* val,
int maxlen,
uint64_t maxval) {
uint64_t byte = (uint8_t)*ptr;
if (UPB_LIKELY((byte & 0x80) == 0)) {
*val = (uint32_t)byte;
return ptr + 1;
}
const char* start = ptr;
_upb_WireReader_ReadLongVarintRet res =
_upb_WireReader_ReadLongVarint(ptr, byte);
if (!res.ptr || (maxlen < 10 && res.ptr - start > maxlen) ||
res.val > maxval) {
return NULL; // Malformed.
}
*val = res.val;
return res.ptr;
}
// Parses a tag into `tag`, and returns a pointer past the end of the tag, or
// NULL if there was an error in the tag data.
//
// REQUIRES: there must be at least 10 bytes of data available at `ptr`.
// Bounds checks must be performed before calling this function, preferably
// by calling upb_EpsCopyInputStream_IsDone().
static UPB_FORCEINLINE const char* upb_WireReader_ReadTag(const char* ptr,
uint32_t* tag) {
uint64_t val;
ptr = _upb_WireReader_ReadVarint(ptr, &val, 5, UINT32_MAX);
if (!ptr) return NULL;
*tag = val;
return ptr;
}
// Given a tag, returns the field number.
UPB_INLINE uint32_t upb_WireReader_GetFieldNumber(uint32_t tag) {
return tag >> kUpb_WireReader_WireTypeBits;
}
// Given a tag, returns the wire type.
UPB_INLINE uint8_t upb_WireReader_GetWireType(uint32_t tag) {
return tag & kUpb_WireReader_WireTypeMask;
}
UPB_INLINE const char* upb_WireReader_ReadVarint(const char* ptr,
uint64_t* val) {
return _upb_WireReader_ReadVarint(ptr, val, 10, UINT64_MAX);
}
// Skips data for a varint, returning a pointer past the end of the varint, or
// NULL if there was an error in the varint data.
//
// REQUIRES: there must be at least 10 bytes of data available at `ptr`.
// Bounds checks must be performed before calling this function, preferably
// by calling upb_EpsCopyInputStream_IsDone().
UPB_INLINE const char* upb_WireReader_SkipVarint(const char* ptr) {
uint64_t val;
return upb_WireReader_ReadVarint(ptr, &val);
}
// Reads a varint indicating the size of a delimited field into `size`, or
// NULL if there was an error in the varint data.
//
// REQUIRES: there must be at least 10 bytes of data available at `ptr`.
// Bounds checks must be performed before calling this function, preferably
// by calling upb_EpsCopyInputStream_IsDone().
UPB_INLINE const char* upb_WireReader_ReadSize(const char* ptr, int* size) {
uint64_t size64;
ptr = upb_WireReader_ReadVarint(ptr, &size64);
if (!ptr || size64 >= INT32_MAX) return NULL;
*size = size64;
return ptr;
}
// Reads a fixed32 field, performing byte swapping if necessary.
//
// REQUIRES: there must be at least 4 bytes of data available at `ptr`.
// Bounds checks must be performed before calling this function, preferably
// by calling upb_EpsCopyInputStream_IsDone().
UPB_INLINE const char* upb_WireReader_ReadFixed32(const char* ptr, void* val) {
uint32_t uval;
memcpy(&uval, ptr, 4);
uval = _upb_BigEndian_Swap32(uval);
memcpy(val, &uval, 4);
return ptr + 4;
}
// Reads a fixed64 field, performing byte swapping if necessary.
//
// REQUIRES: there must be at least 4 bytes of data available at `ptr`.
// Bounds checks must be performed before calling this function, preferably
// by calling upb_EpsCopyInputStream_IsDone().
UPB_INLINE const char* upb_WireReader_ReadFixed64(const char* ptr, void* val) {
uint64_t uval;
memcpy(&uval, ptr, 8);
uval = _upb_BigEndian_Swap64(uval);
memcpy(val, &uval, 8);
return ptr + 8;
}
const char* _upb_WireReader_SkipGroup(const char* ptr, uint32_t tag,
int depth_limit,
upb_EpsCopyInputStream* stream);
// Skips data for a group, returning a pointer past the end of the group, or
// NULL if there was an error parsing the group. The `tag` argument should be
// the start group tag that begins the group. The `depth_limit` argument
// indicates how many levels of recursion the group is allowed to have before
// reporting a parse error (this limit exists to protect against stack
// overflow).
//
// TODO: evaluate how the depth_limit should be specified. Do users need
// control over this?
UPB_INLINE const char* upb_WireReader_SkipGroup(
const char* ptr, uint32_t tag, upb_EpsCopyInputStream* stream) {
return _upb_WireReader_SkipGroup(ptr, tag, 100, stream);
}
UPB_INLINE const char* _upb_WireReader_SkipValue(
const char* ptr, uint32_t tag, int depth_limit,
upb_EpsCopyInputStream* stream) {
switch (upb_WireReader_GetWireType(tag)) {
case kUpb_WireType_Varint:
return upb_WireReader_SkipVarint(ptr);
case kUpb_WireType_32Bit:
return ptr + 4;
case kUpb_WireType_64Bit:
return ptr + 8;
case kUpb_WireType_Delimited: {
int size;
ptr = upb_WireReader_ReadSize(ptr, &size);
if (!ptr) return NULL;
ptr += size;
return ptr;
}
case kUpb_WireType_StartGroup:
return _upb_WireReader_SkipGroup(ptr, tag, depth_limit, stream);
case kUpb_WireType_EndGroup:
return NULL; // Should be handled before now.
default:
return NULL; // Unknown wire type.
}
}
// Skips data for a wire value of any type, returning a pointer past the end of
// the data, or NULL if there was an error parsing the group. The `tag` argument
// should be the tag that was just parsed. The `depth_limit` argument indicates
// how many levels of recursion a group is allowed to have before reporting a
// parse error (this limit exists to protect against stack overflow).
//
// REQUIRES: there must be at least 10 bytes of data available at `ptr`.
// Bounds checks must be performed before calling this function, preferably
// by calling upb_EpsCopyInputStream_IsDone().
//
// TODO: evaluate how the depth_limit should be specified. Do users need
// control over this?
UPB_INLINE const char* upb_WireReader_SkipValue(
const char* ptr, uint32_t tag, upb_EpsCopyInputStream* stream) {
return _upb_WireReader_SkipValue(ptr, tag, 100, stream);
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#include "upb/port/undef.inc"
#endif // UPB_WIRE_READER_H_
|