diff --git a/BUILD b/BUILD index 3136a19106..b8358297e9 100644 --- a/BUILD +++ b/BUILD @@ -109,6 +109,8 @@ cc_library( "upb/encode.c", "upb/internal/table.h", "upb/internal/unicode.h", + "upb/map_sorter.c", + "upb/map_sorter.h", "upb/msg.c", "upb/msg_internal.h", "upb/status.c", @@ -375,6 +377,7 @@ cc_library( hdrs = [ "upb/collections.h", "upb/map.h", + "upb/map_sorter.h", "upb/message_value.h", ], copts = UPB_DEFAULT_COPTS, diff --git a/upb/encode.c b/upb/encode.c index 05f4736c11..a9a1a1094d 100644 --- a/upb/encode.c +++ b/upb/encode.c @@ -33,6 +33,7 @@ #include "upb/extension_registry.h" #include "upb/internal/array.h" +#include "upb/map_sorter.h" #include "upb/msg_internal.h" #include "upb/upb.h" diff --git a/upb/map_sorter.c b/upb/map_sorter.c new file mode 100644 index 0000000000..561981a6bc --- /dev/null +++ b/upb/map_sorter.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "upb/map_sorter.h" + +// Must be last. +#include "upb/port_def.inc" + +static void _upb_mapsorter_getkeys(const void* _a, const void* _b, void* a_key, + void* b_key, size_t size) { + const upb_tabent* const* a = _a; + const upb_tabent* const* b = _b; + upb_StringView a_tabkey = upb_tabstrview((*a)->key); + upb_StringView b_tabkey = upb_tabstrview((*b)->key); + _upb_map_fromkey(a_tabkey, a_key, size); + _upb_map_fromkey(b_tabkey, b_key, size); +} + +static int _upb_mapsorter_cmpi64(const void* _a, const void* _b) { + int64_t a, b; + _upb_mapsorter_getkeys(_a, _b, &a, &b, 8); + return a < b ? -1 : a > b; +} + +static int _upb_mapsorter_cmpu64(const void* _a, const void* _b) { + uint64_t a, b; + _upb_mapsorter_getkeys(_a, _b, &a, &b, 8); + return a < b ? -1 : a > b; +} + +static int _upb_mapsorter_cmpi32(const void* _a, const void* _b) { + int32_t a, b; + _upb_mapsorter_getkeys(_a, _b, &a, &b, 4); + return a < b ? -1 : a > b; +} + +static int _upb_mapsorter_cmpu32(const void* _a, const void* _b) { + uint32_t a, b; + _upb_mapsorter_getkeys(_a, _b, &a, &b, 4); + return a < b ? -1 : a > b; +} + +static int _upb_mapsorter_cmpbool(const void* _a, const void* _b) { + bool a, b; + _upb_mapsorter_getkeys(_a, _b, &a, &b, 1); + return a < b ? -1 : a > b; +} + +static int _upb_mapsorter_cmpstr(const void* _a, const void* _b) { + upb_StringView a, b; + _upb_mapsorter_getkeys(_a, _b, &a, &b, UPB_MAPTYPE_STRING); + size_t common_size = UPB_MIN(a.size, b.size); + int cmp = memcmp(a.data, b.data, common_size); + if (cmp) return -cmp; + return a.size < b.size ? -1 : a.size > b.size; +} + +static int (*const compar[kUpb_FieldType_SizeOf])(const void*, const void*) = { + [kUpb_FieldType_Int64] = _upb_mapsorter_cmpi64, + [kUpb_FieldType_SFixed64] = _upb_mapsorter_cmpi64, + [kUpb_FieldType_SInt64] = _upb_mapsorter_cmpi64, + + [kUpb_FieldType_UInt64] = _upb_mapsorter_cmpu64, + [kUpb_FieldType_Fixed64] = _upb_mapsorter_cmpu64, + + [kUpb_FieldType_Int32] = _upb_mapsorter_cmpi32, + [kUpb_FieldType_SInt32] = _upb_mapsorter_cmpi32, + [kUpb_FieldType_SFixed32] = _upb_mapsorter_cmpi32, + [kUpb_FieldType_Enum] = _upb_mapsorter_cmpi32, + + [kUpb_FieldType_UInt32] = _upb_mapsorter_cmpu32, + [kUpb_FieldType_Fixed32] = _upb_mapsorter_cmpu32, + + [kUpb_FieldType_Bool] = _upb_mapsorter_cmpbool, + + [kUpb_FieldType_String] = _upb_mapsorter_cmpstr, + [kUpb_FieldType_Bytes] = _upb_mapsorter_cmpstr, +}; + +bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type, + const upb_Map* map, _upb_sortedmap* sorted) { + int map_size = _upb_Map_Size(map); + sorted->start = s->size; + sorted->pos = sorted->start; + sorted->end = sorted->start + map_size; + + // Grow s->entries if necessary. + if (sorted->end > s->cap) { + s->cap = _upb_Log2CeilingSize(sorted->end); + s->entries = realloc(s->entries, s->cap * sizeof(*s->entries)); + if (!s->entries) return false; + } + + s->size = sorted->end; + + // Copy non-empty entries from the table to s->entries. + upb_tabent const** dst = &s->entries[sorted->start]; + const upb_tabent* src = map->table.t.entries; + const upb_tabent* end = src + upb_table_size(&map->table.t); + for (; src < end; src++) { + if (!upb_tabent_isempty(src)) { + *dst = src; + dst++; + } + } + UPB_ASSERT(dst == &s->entries[sorted->end]); + + // Sort entries according to the key type. + qsort(&s->entries[sorted->start], map_size, sizeof(*s->entries), + compar[key_type]); + return true; +} diff --git a/upb/map_sorter.h b/upb/map_sorter.h new file mode 100644 index 0000000000..07c0bcafcf --- /dev/null +++ b/upb/map_sorter.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// EVERYTHING BELOW THIS LINE IS INTERNAL - DO NOT USE ///////////////////////// + +#ifndef UPB_MAP_SORTER_H_ +#define UPB_MAP_SORTER_H_ + +#include "upb/msg_internal.h" + +// Must be last. +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +// _upb_mapsorter sorts maps and provides ordered iteration over the entries. +// Since maps can be recursive (map values can be messages which contain other +// maps), _upb_mapsorter can contain a stack of maps. + +typedef struct { + upb_tabent const** entries; + int size; + int cap; +} _upb_mapsorter; + +typedef struct { + int start; + int pos; + int end; +} _upb_sortedmap; + +UPB_INLINE void _upb_mapsorter_init(_upb_mapsorter* s) { + s->entries = NULL; + s->size = 0; + s->cap = 0; +} + +UPB_INLINE void _upb_mapsorter_destroy(_upb_mapsorter* s) { + if (s->entries) free(s->entries); +} + +UPB_INLINE bool _upb_sortedmap_next(_upb_mapsorter* s, const upb_Map* map, + _upb_sortedmap* sorted, upb_MapEntry* ent) { + if (sorted->pos == sorted->end) return false; + const upb_tabent* tabent = s->entries[sorted->pos++]; + upb_StringView key = upb_tabstrview(tabent->key); + _upb_map_fromkey(key, &ent->k, map->key_size); + upb_value val = {tabent->val.val}; + _upb_map_fromvalue(val, &ent->v, map->val_size); + return true; +} + +UPB_INLINE void _upb_mapsorter_popmap(_upb_mapsorter* s, + _upb_sortedmap* sorted) { + s->size = sorted->start; +} + +bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type, + const upb_Map* map, _upb_sortedmap* sorted); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port_undef.inc" + +#endif /* UPB_MAP_SORTER_H_ */ diff --git a/upb/msg.c b/upb/msg.c index 714cc79208..53ea32217b 100644 --- a/upb/msg.c +++ b/upb/msg.c @@ -192,125 +192,5 @@ upb_Map* _upb_Map_New(upb_Arena* a, size_t key_size, size_t value_size) { return map; } -static void _upb_mapsorter_getkeys(const void* _a, const void* _b, void* a_key, - void* b_key, size_t size) { - const upb_tabent* const* a = _a; - const upb_tabent* const* b = _b; - upb_StringView a_tabkey = upb_tabstrview((*a)->key); - upb_StringView b_tabkey = upb_tabstrview((*b)->key); - _upb_map_fromkey(a_tabkey, a_key, size); - _upb_map_fromkey(b_tabkey, b_key, size); -} - -#define UPB_COMPARE_INTEGERS(a, b) ((a) < (b) ? -1 : ((a) == (b) ? 0 : 1)) - -static int _upb_mapsorter_cmpi64(const void* _a, const void* _b) { - int64_t a, b; - _upb_mapsorter_getkeys(_a, _b, &a, &b, 8); - return UPB_COMPARE_INTEGERS(a, b); -} - -static int _upb_mapsorter_cmpu64(const void* _a, const void* _b) { - uint64_t a, b; - _upb_mapsorter_getkeys(_a, _b, &a, &b, 8); - return UPB_COMPARE_INTEGERS(a, b); -} - -static int _upb_mapsorter_cmpi32(const void* _a, const void* _b) { - int32_t a, b; - _upb_mapsorter_getkeys(_a, _b, &a, &b, 4); - return UPB_COMPARE_INTEGERS(a, b); -} - -static int _upb_mapsorter_cmpu32(const void* _a, const void* _b) { - uint32_t a, b; - _upb_mapsorter_getkeys(_a, _b, &a, &b, 4); - return UPB_COMPARE_INTEGERS(a, b); -} - -static int _upb_mapsorter_cmpbool(const void* _a, const void* _b) { - bool a, b; - _upb_mapsorter_getkeys(_a, _b, &a, &b, 1); - return UPB_COMPARE_INTEGERS(a, b); -} - -static int _upb_mapsorter_cmpstr(const void* _a, const void* _b) { - upb_StringView a, b; - _upb_mapsorter_getkeys(_a, _b, &a, &b, UPB_MAPTYPE_STRING); - size_t common_size = UPB_MIN(a.size, b.size); - int cmp = memcmp(a.data, b.data, common_size); - if (cmp) return -cmp; - return UPB_COMPARE_INTEGERS(a.size, b.size); -} - -#undef UPB_COMPARE_INTEGERS - -bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type, - const upb_Map* map, _upb_sortedmap* sorted) { - int map_size = _upb_Map_Size(map); - sorted->start = s->size; - sorted->pos = sorted->start; - sorted->end = sorted->start + map_size; - - /* Grow s->entries if necessary. */ - if (sorted->end > s->cap) { - s->cap = _upb_Log2CeilingSize(sorted->end); - s->entries = realloc(s->entries, s->cap * sizeof(*s->entries)); - if (!s->entries) return false; - } - - s->size = sorted->end; - - /* Copy non-empty entries from the table to s->entries. */ - upb_tabent const** dst = &s->entries[sorted->start]; - const upb_tabent* src = map->table.t.entries; - const upb_tabent* end = src + upb_table_size(&map->table.t); - for (; src < end; src++) { - if (!upb_tabent_isempty(src)) { - *dst = src; - dst++; - } - } - UPB_ASSERT(dst == &s->entries[sorted->end]); - - /* Sort entries according to the key type. */ - - int (*compar)(const void*, const void*); - - switch (key_type) { - case kUpb_FieldType_Int64: - case kUpb_FieldType_SFixed64: - case kUpb_FieldType_SInt64: - compar = _upb_mapsorter_cmpi64; - break; - case kUpb_FieldType_UInt64: - case kUpb_FieldType_Fixed64: - compar = _upb_mapsorter_cmpu64; - break; - case kUpb_FieldType_Int32: - case kUpb_FieldType_SInt32: - case kUpb_FieldType_SFixed32: - case kUpb_FieldType_Enum: - compar = _upb_mapsorter_cmpi32; - break; - case kUpb_FieldType_UInt32: - case kUpb_FieldType_Fixed32: - compar = _upb_mapsorter_cmpu32; - break; - case kUpb_FieldType_Bool: - compar = _upb_mapsorter_cmpbool; - break; - case kUpb_FieldType_String: - case kUpb_FieldType_Bytes: - compar = _upb_mapsorter_cmpstr; - break; - default: - UPB_UNREACHABLE(); - } - - qsort(&s->entries[sorted->start], map_size, sizeof(*s->entries), compar); - return true; -} - const float kUpb_FltInfinity = INFINITY; const double kUpb_Infinity = INFINITY; diff --git a/upb/msg_internal.h b/upb/msg_internal.h index 85bac968a1..ccf44ac113 100644 --- a/upb/msg_internal.h +++ b/upb/msg_internal.h @@ -650,53 +650,6 @@ UPB_INLINE void _upb_msg_map_set_value(void* msg, const void* val, } } -/** _upb_mapsorter ************************************************************/ - -/* _upb_mapsorter sorts maps and provides ordered iteration over the entries. - * Since maps can be recursive (map values can be messages which contain other - * maps). _upb_mapsorter can contain a stack of maps. */ - -typedef struct { - upb_tabent const** entries; - int size; - int cap; -} _upb_mapsorter; - -typedef struct { - int start; - int pos; - int end; -} _upb_sortedmap; - -UPB_INLINE void _upb_mapsorter_init(_upb_mapsorter* s) { - s->entries = NULL; - s->size = 0; - s->cap = 0; -} - -UPB_INLINE void _upb_mapsorter_destroy(_upb_mapsorter* s) { - if (s->entries) free(s->entries); -} - -bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type, - const upb_Map* map, _upb_sortedmap* sorted); - -UPB_INLINE void _upb_mapsorter_popmap(_upb_mapsorter* s, - _upb_sortedmap* sorted) { - s->size = sorted->start; -} - -UPB_INLINE bool _upb_sortedmap_next(_upb_mapsorter* s, const upb_Map* map, - _upb_sortedmap* sorted, upb_MapEntry* ent) { - if (sorted->pos == sorted->end) return false; - const upb_tabent* tabent = s->entries[sorted->pos++]; - upb_StringView key = upb_tabstrview(tabent->key); - _upb_map_fromkey(key, &ent->k, map->key_size); - upb_value val = {tabent->val.val}; - _upb_map_fromvalue(val, &ent->v, map->val_size); - return true; -} - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/upb/text_encode.c b/upb/text_encode.c index d3e9f0145c..45acc34f5b 100644 --- a/upb/text_encode.c +++ b/upb/text_encode.c @@ -37,6 +37,7 @@ #include "upb/internal/encode.h" #include "upb/internal/vsnprintf_compat.h" #include "upb/map.h" +#include "upb/map_sorter.h" #include "upb/reflection/message.h" // Must be last. diff --git a/upb/upb.h b/upb/upb.h index e77fd40bdb..c6c3275759 100644 --- a/upb/upb.h +++ b/upb/upb.h @@ -111,9 +111,11 @@ typedef enum { kUpb_FieldType_SFixed32 = 15, kUpb_FieldType_SFixed64 = 16, kUpb_FieldType_SInt32 = 17, - kUpb_FieldType_SInt64 = 18 + kUpb_FieldType_SInt64 = 18, } upb_FieldType; +#define kUpb_FieldType_SizeOf 19 + #define kUpb_Map_Begin ((size_t)-1) UPB_INLINE bool _upb_IsLittleEndian(void) {