OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * |
| 3 * Copyright 2015-2016, Google Inc. |
| 4 * All rights reserved. |
| 5 * |
| 6 * Redistribution and use in source and binary forms, with or without |
| 7 * modification, are permitted provided that the following conditions are |
| 8 * met: |
| 9 * |
| 10 * * Redistributions of source code must retain the above copyright |
| 11 * notice, this list of conditions and the following disclaimer. |
| 12 * * Redistributions in binary form must reproduce the above |
| 13 * copyright notice, this list of conditions and the following disclaimer |
| 14 * in the documentation and/or other materials provided with the |
| 15 * distribution. |
| 16 * * Neither the name of Google Inc. nor the names of its |
| 17 * contributors may be used to endorse or promote products derived from |
| 18 * this software without specific prior written permission. |
| 19 * |
| 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 * |
| 32 */ |
| 33 |
| 34 #include <grpc/census.h> |
| 35 #include <grpc/support/alloc.h> |
| 36 #include <grpc/support/log.h> |
| 37 #include <grpc/support/port_platform.h> |
| 38 #include <grpc/support/useful.h> |
| 39 #include <stdbool.h> |
| 40 #include <string.h> |
| 41 #include "src/core/support/string.h" |
| 42 |
| 43 // Functions in this file support the public context API, including |
| 44 // encoding/decoding as part of context propagation across RPC's. The overall |
| 45 // requirements (in approximate priority order) for the |
| 46 // context representation: |
| 47 // 1. Efficient conversion to/from wire format |
| 48 // 2. Minimal bytes used on-wire |
| 49 // 3. Efficient context creation |
| 50 // 4. Efficient lookup of tag value for a key |
| 51 // 5. Efficient iteration over tags |
| 52 // 6. Minimal memory footprint |
| 53 // |
| 54 // Notes on tradeoffs/decisions: |
| 55 // * tag includes 1 byte length of key, as well as nil-terminating byte. These |
| 56 // are to aid in efficient parsing and the ability to directly return key |
| 57 // strings. This is more important than saving a single byte/tag on the wire. |
| 58 // * The wire encoding uses only single byte values. This eliminates the need |
| 59 // to handle endian-ness conversions. It also means there is a hard upper |
| 60 // limit of 255 for both CENSUS_MAX_TAG_KV_LEN and CENSUS_MAX_PROPAGATED_TAGS. |
| 61 // * Keep all tag information (keys/values/flags) in a single memory buffer, |
| 62 // that can be directly copied to the wire. |
| 63 |
| 64 // min and max valid chars in tag keys and values. All printable ASCII is OK. |
| 65 #define MIN_VALID_TAG_CHAR 32 // ' ' |
| 66 #define MAX_VALID_TAG_CHAR 126 // '~' |
| 67 |
| 68 // Structure representing a set of tags. Essentially a count of number of tags |
| 69 // present, and pointer to a chunk of memory that contains the per-tag details. |
| 70 struct tag_set { |
| 71 int ntags; // number of tags. |
| 72 int ntags_alloc; // ntags + number of deleted tags (total number of tags |
| 73 // in all of kvm). This will always be == ntags, except during the process |
| 74 // of building a new tag set. |
| 75 size_t kvm_size; // number of bytes allocated for key/value storage. |
| 76 size_t kvm_used; // number of bytes of used key/value memory |
| 77 char *kvm; // key/value memory. Consists of repeated entries of: |
| 78 // Offset Size Description |
| 79 // 0 1 Key length, including trailing 0. (K) |
| 80 // 1 1 Value length, including trailing 0 (V) |
| 81 // 2 1 Flags |
| 82 // 3 K Key bytes |
| 83 // 3 + K V Value bytes |
| 84 // |
| 85 // We refer to the first 3 entries as the 'tag header'. If extra values are |
| 86 // introduced in the header, you will need to modify the TAG_HEADER_SIZE |
| 87 // constant, the raw_tag structure (and everything that uses it) and the |
| 88 // encode/decode functions appropriately. |
| 89 }; |
| 90 |
| 91 // Number of bytes in tag header. |
| 92 #define TAG_HEADER_SIZE 3 // key length (1) + value length (1) + flags (1) |
| 93 // Offsets to tag header entries. |
| 94 #define KEY_LEN_OFFSET 0 |
| 95 #define VALUE_LEN_OFFSET 1 |
| 96 #define FLAG_OFFSET 2 |
| 97 |
| 98 // raw_tag represents the raw-storage form of a tag in the kvm of a tag_set. |
| 99 struct raw_tag { |
| 100 uint8_t key_len; |
| 101 uint8_t value_len; |
| 102 uint8_t flags; |
| 103 char *key; |
| 104 char *value; |
| 105 }; |
| 106 |
| 107 // Use a reserved flag bit for indication of deleted tag. |
| 108 #define CENSUS_TAG_DELETED CENSUS_TAG_RESERVED |
| 109 #define CENSUS_TAG_IS_DELETED(flags) (flags & CENSUS_TAG_DELETED) |
| 110 |
| 111 // Primary representation of a context. Composed of 2 underlying tag_set |
| 112 // structs, one each for propagated and local (non-propagated) tags. This is |
| 113 // to efficiently support tag encoding/decoding. |
| 114 // TODO(aveitch): need to add tracing id's/structure. |
| 115 struct census_context { |
| 116 struct tag_set tags[2]; |
| 117 census_context_status status; |
| 118 }; |
| 119 |
| 120 // Indices into the tags member of census_context |
| 121 #define PROPAGATED_TAGS 0 |
| 122 #define LOCAL_TAGS 1 |
| 123 |
| 124 // Validate (check all characters are in range and size is less than limit) a |
| 125 // key or value string. Returns 0 if the string is invalid, or the length |
| 126 // (including terminator) if valid. |
| 127 static size_t validate_tag(const char *kv) { |
| 128 size_t len = 1; |
| 129 char ch; |
| 130 while ((ch = *kv++) != 0) { |
| 131 if (ch < MIN_VALID_TAG_CHAR || ch > MAX_VALID_TAG_CHAR) { |
| 132 return 0; |
| 133 } |
| 134 len++; |
| 135 } |
| 136 if (len > CENSUS_MAX_TAG_KV_LEN) { |
| 137 return 0; |
| 138 } |
| 139 return len; |
| 140 } |
| 141 |
| 142 // Extract a raw tag given a pointer (raw) to the tag header. Allow for some |
| 143 // extra bytes in the tag header (see encode/decode functions for usage: this |
| 144 // allows for future expansion of the tag header). |
| 145 static char *decode_tag(struct raw_tag *tag, char *header, int offset) { |
| 146 tag->key_len = (uint8_t)(*header++); |
| 147 tag->value_len = (uint8_t)(*header++); |
| 148 tag->flags = (uint8_t)(*header++); |
| 149 header += offset; |
| 150 tag->key = header; |
| 151 header += tag->key_len; |
| 152 tag->value = header; |
| 153 return header + tag->value_len; |
| 154 } |
| 155 |
| 156 // Make a copy (in 'to') of an existing tag_set. |
| 157 static void tag_set_copy(struct tag_set *to, const struct tag_set *from) { |
| 158 memcpy(to, from, sizeof(struct tag_set)); |
| 159 to->kvm = gpr_malloc(to->kvm_size); |
| 160 memcpy(to->kvm, from->kvm, from->kvm_used); |
| 161 } |
| 162 |
| 163 // Delete a tag from a tag_set, if it exists (returns true if it did). |
| 164 static bool tag_set_delete_tag(struct tag_set *tags, const char *key, |
| 165 size_t key_len) { |
| 166 char *kvp = tags->kvm; |
| 167 for (int i = 0; i < tags->ntags_alloc; i++) { |
| 168 uint8_t *flags = (uint8_t *)(kvp + FLAG_OFFSET); |
| 169 struct raw_tag tag; |
| 170 kvp = decode_tag(&tag, kvp, 0); |
| 171 if (CENSUS_TAG_IS_DELETED(tag.flags)) continue; |
| 172 if ((key_len == tag.key_len) && (memcmp(key, tag.key, key_len) == 0)) { |
| 173 *flags |= CENSUS_TAG_DELETED; |
| 174 tags->ntags--; |
| 175 return true; |
| 176 } |
| 177 } |
| 178 return false; |
| 179 } |
| 180 |
| 181 // Delete a tag from a context, return true if it existed. |
| 182 static bool context_delete_tag(census_context *context, const census_tag *tag, |
| 183 size_t key_len) { |
| 184 return ( |
| 185 tag_set_delete_tag(&context->tags[LOCAL_TAGS], tag->key, key_len) || |
| 186 tag_set_delete_tag(&context->tags[PROPAGATED_TAGS], tag->key, key_len)); |
| 187 } |
| 188 |
| 189 // Add a tag to a tag_set. Return true on success, false if the tag could |
| 190 // not be added because of constraints on tag set size. This function should |
| 191 // not be called if the tag may already exist (in a non-deleted state) in |
| 192 // the tag_set, as that would result in two tags with the same key. |
| 193 static bool tag_set_add_tag(struct tag_set *tags, const census_tag *tag, |
| 194 size_t key_len, size_t value_len) { |
| 195 if (tags->ntags == CENSUS_MAX_PROPAGATED_TAGS) { |
| 196 return false; |
| 197 } |
| 198 const size_t tag_size = key_len + value_len + TAG_HEADER_SIZE; |
| 199 if (tags->kvm_used + tag_size > tags->kvm_size) { |
| 200 // allocate new memory if needed |
| 201 tags->kvm_size += 2 * CENSUS_MAX_TAG_KV_LEN + TAG_HEADER_SIZE; |
| 202 char *new_kvm = gpr_malloc(tags->kvm_size); |
| 203 memcpy(new_kvm, tags->kvm, tags->kvm_used); |
| 204 gpr_free(tags->kvm); |
| 205 tags->kvm = new_kvm; |
| 206 } |
| 207 char *kvp = tags->kvm + tags->kvm_used; |
| 208 *kvp++ = (char)key_len; |
| 209 *kvp++ = (char)value_len; |
| 210 // ensure reserved flags are not used. |
| 211 *kvp++ = (char)(tag->flags & (CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS)); |
| 212 memcpy(kvp, tag->key, key_len); |
| 213 kvp += key_len; |
| 214 memcpy(kvp, tag->value, value_len); |
| 215 tags->kvm_used += tag_size; |
| 216 tags->ntags++; |
| 217 tags->ntags_alloc++; |
| 218 return true; |
| 219 } |
| 220 |
| 221 // Add/modify/delete a tag to/in a context. Caller must validate that tag key |
| 222 // etc. are valid. |
| 223 static void context_modify_tag(census_context *context, const census_tag *tag, |
| 224 size_t key_len, size_t value_len) { |
| 225 // First delete the tag if it is already present. |
| 226 bool deleted = context_delete_tag(context, tag, key_len); |
| 227 bool added = false; |
| 228 if (CENSUS_TAG_IS_PROPAGATED(tag->flags)) { |
| 229 added = tag_set_add_tag(&context->tags[PROPAGATED_TAGS], tag, key_len, |
| 230 value_len); |
| 231 } else { |
| 232 added = |
| 233 tag_set_add_tag(&context->tags[LOCAL_TAGS], tag, key_len, value_len); |
| 234 } |
| 235 |
| 236 if (deleted) { |
| 237 context->status.n_modified_tags++; |
| 238 } else { |
| 239 if (added) { |
| 240 context->status.n_added_tags++; |
| 241 } else { |
| 242 context->status.n_ignored_tags++; |
| 243 } |
| 244 } |
| 245 } |
| 246 |
| 247 // Remove memory used for deleted tags from a tag set. Basic algorithm: |
| 248 // 1) Walk through tag set to find first deleted tag. Record where it is. |
| 249 // 2) Find the next not-deleted tag. Copy all of kvm from there to the end |
| 250 // "over" the deleted tags |
| 251 // 3) repeat #1 and #2 until we have seen all tags |
| 252 // 4) if we are still looking for a not-deleted tag, then all the end portion |
| 253 // of the kvm is deleted. Just reduce the used amount of memory by the |
| 254 // appropriate amount. |
| 255 static void tag_set_flatten(struct tag_set *tags) { |
| 256 if (tags->ntags == tags->ntags_alloc) return; |
| 257 bool found_deleted = false; // found a deleted tag. |
| 258 char *kvp = tags->kvm; |
| 259 char *dbase = NULL; // record location of deleted tag |
| 260 for (int i = 0; i < tags->ntags_alloc; i++) { |
| 261 struct raw_tag tag; |
| 262 char *next_kvp = decode_tag(&tag, kvp, 0); |
| 263 if (found_deleted) { |
| 264 if (!CENSUS_TAG_IS_DELETED(tag.flags)) { |
| 265 ptrdiff_t reduce = kvp - dbase; // #bytes in deleted tags |
| 266 GPR_ASSERT(reduce > 0); |
| 267 ptrdiff_t copy_size = tags->kvm + tags->kvm_used - kvp; |
| 268 GPR_ASSERT(copy_size > 0); |
| 269 memmove(dbase, kvp, (size_t)copy_size); |
| 270 tags->kvm_used -= (size_t)reduce; |
| 271 next_kvp -= reduce; |
| 272 found_deleted = false; |
| 273 } |
| 274 } else { |
| 275 if (CENSUS_TAG_IS_DELETED(tag.flags)) { |
| 276 dbase = kvp; |
| 277 found_deleted = true; |
| 278 } |
| 279 } |
| 280 kvp = next_kvp; |
| 281 } |
| 282 if (found_deleted) { |
| 283 GPR_ASSERT(dbase > tags->kvm); |
| 284 tags->kvm_used = (size_t)(dbase - tags->kvm); |
| 285 } |
| 286 tags->ntags_alloc = tags->ntags; |
| 287 } |
| 288 |
| 289 census_context *census_context_create(const census_context *base, |
| 290 const census_tag *tags, int ntags, |
| 291 census_context_status const **status) { |
| 292 census_context *context = gpr_malloc(sizeof(census_context)); |
| 293 // If we are given a base, copy it into our new tag set. Otherwise set it |
| 294 // to zero/NULL everything. |
| 295 if (base == NULL) { |
| 296 memset(context, 0, sizeof(census_context)); |
| 297 } else { |
| 298 tag_set_copy(&context->tags[PROPAGATED_TAGS], &base->tags[PROPAGATED_TAGS]); |
| 299 tag_set_copy(&context->tags[LOCAL_TAGS], &base->tags[LOCAL_TAGS]); |
| 300 memset(&context->status, 0, sizeof(context->status)); |
| 301 } |
| 302 // Walk over the additional tags and, for those that aren't invalid, modify |
| 303 // the context to add/replace/delete as required. |
| 304 for (int i = 0; i < ntags; i++) { |
| 305 const census_tag *tag = &tags[i]; |
| 306 size_t key_len = validate_tag(tag->key); |
| 307 // ignore the tag if it is invalid or too short. |
| 308 if (key_len <= 1) { |
| 309 context->status.n_invalid_tags++; |
| 310 } else { |
| 311 if (tag->value != NULL) { |
| 312 size_t value_len = validate_tag(tag->value); |
| 313 if (value_len != 0) { |
| 314 context_modify_tag(context, tag, key_len, value_len); |
| 315 } else { |
| 316 context->status.n_invalid_tags++; |
| 317 } |
| 318 } else { |
| 319 if (context_delete_tag(context, tag, key_len)) { |
| 320 context->status.n_deleted_tags++; |
| 321 } |
| 322 } |
| 323 } |
| 324 } |
| 325 // Remove any deleted tags, update status if needed, and return. |
| 326 tag_set_flatten(&context->tags[PROPAGATED_TAGS]); |
| 327 tag_set_flatten(&context->tags[LOCAL_TAGS]); |
| 328 context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags; |
| 329 context->status.n_local_tags = context->tags[LOCAL_TAGS].ntags; |
| 330 if (status) { |
| 331 *status = &context->status; |
| 332 } |
| 333 return context; |
| 334 } |
| 335 |
| 336 const census_context_status *census_context_get_status( |
| 337 const census_context *context) { |
| 338 return &context->status; |
| 339 } |
| 340 |
| 341 void census_context_destroy(census_context *context) { |
| 342 gpr_free(context->tags[PROPAGATED_TAGS].kvm); |
| 343 gpr_free(context->tags[LOCAL_TAGS].kvm); |
| 344 gpr_free(context); |
| 345 } |
| 346 |
| 347 void census_context_initialize_iterator(const census_context *context, |
| 348 census_context_iterator *iterator) { |
| 349 iterator->context = context; |
| 350 iterator->index = 0; |
| 351 if (context->tags[PROPAGATED_TAGS].ntags != 0) { |
| 352 iterator->base = PROPAGATED_TAGS; |
| 353 iterator->kvm = context->tags[PROPAGATED_TAGS].kvm; |
| 354 } else if (context->tags[LOCAL_TAGS].ntags != 0) { |
| 355 iterator->base = LOCAL_TAGS; |
| 356 iterator->kvm = context->tags[LOCAL_TAGS].kvm; |
| 357 } else { |
| 358 iterator->base = -1; |
| 359 } |
| 360 } |
| 361 |
| 362 int census_context_next_tag(census_context_iterator *iterator, |
| 363 census_tag *tag) { |
| 364 if (iterator->base < 0) { |
| 365 return 0; |
| 366 } |
| 367 struct raw_tag raw; |
| 368 iterator->kvm = decode_tag(&raw, iterator->kvm, 0); |
| 369 tag->key = raw.key; |
| 370 tag->value = raw.value; |
| 371 tag->flags = raw.flags; |
| 372 if (++iterator->index == iterator->context->tags[iterator->base].ntags) { |
| 373 do { |
| 374 if (iterator->base == LOCAL_TAGS) { |
| 375 iterator->base = -1; |
| 376 return 1; |
| 377 } |
| 378 } while (iterator->context->tags[++iterator->base].ntags == 0); |
| 379 iterator->index = 0; |
| 380 iterator->kvm = iterator->context->tags[iterator->base].kvm; |
| 381 } |
| 382 return 1; |
| 383 } |
| 384 |
| 385 // Find a tag in a tag_set by key. Return true if found, false otherwise. |
| 386 static bool tag_set_get_tag(const struct tag_set *tags, const char *key, |
| 387 size_t key_len, census_tag *tag) { |
| 388 char *kvp = tags->kvm; |
| 389 for (int i = 0; i < tags->ntags; i++) { |
| 390 struct raw_tag raw; |
| 391 kvp = decode_tag(&raw, kvp, 0); |
| 392 if (key_len == raw.key_len && memcmp(raw.key, key, key_len) == 0) { |
| 393 tag->key = raw.key; |
| 394 tag->value = raw.value; |
| 395 tag->flags = raw.flags; |
| 396 return true; |
| 397 } |
| 398 } |
| 399 return false; |
| 400 } |
| 401 |
| 402 int census_context_get_tag(const census_context *context, const char *key, |
| 403 census_tag *tag) { |
| 404 size_t key_len = strlen(key) + 1; |
| 405 if (key_len == 1) { |
| 406 return 0; |
| 407 } |
| 408 if (tag_set_get_tag(&context->tags[PROPAGATED_TAGS], key, key_len, tag) || |
| 409 tag_set_get_tag(&context->tags[LOCAL_TAGS], key, key_len, tag)) { |
| 410 return 1; |
| 411 } |
| 412 return 0; |
| 413 } |
| 414 |
| 415 // Context encoding and decoding functions. |
| 416 // |
| 417 // Wire format for tag_set's on the wire: |
| 418 // |
| 419 // First, a tag set header: |
| 420 // |
| 421 // offset bytes description |
| 422 // 0 1 version number |
| 423 // 1 1 number of bytes in this header. This allows for future |
| 424 // expansion. |
| 425 // 2 1 number of bytes in each tag header. |
| 426 // 3 1 ntags value from tag set. |
| 427 // |
| 428 // This is followed by the key/value memory from struct tag_set. |
| 429 |
| 430 #define ENCODED_VERSION 0 // Version number |
| 431 #define ENCODED_HEADER_SIZE 4 // size of tag set header |
| 432 |
| 433 // Encode a tag set. Returns 0 if buffer is too small. |
| 434 static size_t tag_set_encode(const struct tag_set *tags, char *buffer, |
| 435 size_t buf_size) { |
| 436 if (buf_size < ENCODED_HEADER_SIZE + tags->kvm_used) { |
| 437 return 0; |
| 438 } |
| 439 buf_size -= ENCODED_HEADER_SIZE; |
| 440 *buffer++ = (char)ENCODED_VERSION; |
| 441 *buffer++ = (char)ENCODED_HEADER_SIZE; |
| 442 *buffer++ = (char)TAG_HEADER_SIZE; |
| 443 *buffer++ = (char)tags->ntags; |
| 444 if (tags->ntags == 0) { |
| 445 return ENCODED_HEADER_SIZE; |
| 446 } |
| 447 memcpy(buffer, tags->kvm, tags->kvm_used); |
| 448 return ENCODED_HEADER_SIZE + tags->kvm_used; |
| 449 } |
| 450 |
| 451 size_t census_context_encode(const census_context *context, char *buffer, |
| 452 size_t buf_size) { |
| 453 return tag_set_encode(&context->tags[PROPAGATED_TAGS], buffer, buf_size); |
| 454 } |
| 455 |
| 456 // Decode a tag set. |
| 457 static void tag_set_decode(struct tag_set *tags, const char *buffer, |
| 458 size_t size) { |
| 459 uint8_t version = (uint8_t)(*buffer++); |
| 460 uint8_t header_size = (uint8_t)(*buffer++); |
| 461 uint8_t tag_header_size = (uint8_t)(*buffer++); |
| 462 tags->ntags = tags->ntags_alloc = (int)(*buffer++); |
| 463 if (tags->ntags == 0) { |
| 464 tags->ntags_alloc = 0; |
| 465 tags->kvm_size = 0; |
| 466 tags->kvm_used = 0; |
| 467 tags->kvm = NULL; |
| 468 return; |
| 469 } |
| 470 if (header_size != ENCODED_HEADER_SIZE) { |
| 471 GPR_ASSERT(version != ENCODED_VERSION); |
| 472 GPR_ASSERT(ENCODED_HEADER_SIZE < header_size); |
| 473 buffer += (header_size - ENCODED_HEADER_SIZE); |
| 474 } |
| 475 tags->kvm_used = size - header_size; |
| 476 tags->kvm_size = tags->kvm_used + CENSUS_MAX_TAG_KV_LEN; |
| 477 tags->kvm = gpr_malloc(tags->kvm_size); |
| 478 if (tag_header_size != TAG_HEADER_SIZE) { |
| 479 // something new in the tag information. I don't understand it, so |
| 480 // don't copy it over. |
| 481 GPR_ASSERT(version != ENCODED_VERSION); |
| 482 GPR_ASSERT(tag_header_size > TAG_HEADER_SIZE); |
| 483 char *kvp = tags->kvm; |
| 484 for (int i = 0; i < tags->ntags; i++) { |
| 485 memcpy(kvp, buffer, TAG_HEADER_SIZE); |
| 486 kvp += header_size; |
| 487 struct raw_tag raw; |
| 488 buffer = |
| 489 decode_tag(&raw, (char *)buffer, tag_header_size - TAG_HEADER_SIZE); |
| 490 memcpy(kvp, raw.key, (size_t)raw.key_len + raw.value_len); |
| 491 kvp += raw.key_len + raw.value_len; |
| 492 } |
| 493 } else { |
| 494 memcpy(tags->kvm, buffer, tags->kvm_used); |
| 495 } |
| 496 } |
| 497 |
| 498 census_context *census_context_decode(const char *buffer, size_t size) { |
| 499 census_context *context = gpr_malloc(sizeof(census_context)); |
| 500 memset(&context->tags[LOCAL_TAGS], 0, sizeof(struct tag_set)); |
| 501 if (buffer == NULL) { |
| 502 memset(&context->tags[PROPAGATED_TAGS], 0, sizeof(struct tag_set)); |
| 503 } else { |
| 504 tag_set_decode(&context->tags[PROPAGATED_TAGS], buffer, size); |
| 505 } |
| 506 memset(&context->status, 0, sizeof(context->status)); |
| 507 context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags; |
| 508 return context; |
| 509 } |
OLD | NEW |