parent
38b6eeeb4e
commit
bb30d2591f
19 changed files with 1408 additions and 47 deletions
@ -0,0 +1,527 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* 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 Inc. 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 THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS 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 "tag_set.h" |
||||
|
||||
#include <grpc/census.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpc/support/sync.h> |
||||
#include <grpc/support/useful.h> |
||||
#include <stdbool.h> |
||||
#include <string.h> |
||||
#include "src/core/support/murmur_hash.h" |
||||
#include "src/core/support/string.h" |
||||
|
||||
// Functions in this file support the public tag_set API, as well as
|
||||
// encoding/decoding tag_sets as part of context transmission across
|
||||
// RPC's. The overall requirements (in approximate priority order) for the
|
||||
// tag_set representations:
|
||||
// 1. Efficient conversion to/from wire format
|
||||
// 2. Minimal bytes used on-wire
|
||||
// 3. Efficient tag set creation
|
||||
// 4. Efficient lookup of value for a key
|
||||
// 5. Efficient lookup of value for an index (to support iteration)
|
||||
// 6. Minimal memory footprint
|
||||
//
|
||||
// Notes on tradeoffs/decisions:
|
||||
// * tag includes 1 byte length of key, as well as nil-terminating byte. These
|
||||
// are to aid in efficient parsing and the ability to directly return key
|
||||
// strings. This is more important than saving a single byte/tag on the wire.
|
||||
// * The wire encoding uses only single byte values. This eliminates the need
|
||||
// to handle endian-ness conversions.
|
||||
// * Keep all tag information (keys/values/flags) in a single memory buffer,
|
||||
// that can be directly copied to the wire. This makes iteration by index
|
||||
// somewhat less efficient.
|
||||
// * Binary tags are encoded seperately from non-binary tags. There are a
|
||||
// primarily because non-binary tags are far more likely to be repeated
|
||||
// across multiple RPC calls, so are more efficiently cached and
|
||||
// compressed in any metadata schemes.
|
||||
// * deleted/modified tags are kept in memory, just marked with a deleted
|
||||
// flag. This enables faster processing TODO: benchmark this
|
||||
// * all lengths etc. are restricted to one byte. This eliminates endian
|
||||
// issues.
|
||||
|
||||
// Structure representing a set of tags. Essentially a count of number of tags
|
||||
// present, and pointer to a chunk of memory that contains the per-tag details.
|
||||
struct tag_set { |
||||
int ntags; // number of tags.
|
||||
int ntags_alloc; // ntags + number of deleted tags (total number of tags
|
||||
// in all of kvm).
|
||||
size_t kvm_size; // number of bytes allocated for key/value storage.
|
||||
size_t kvm_used; // number of bytes of used key/value memory
|
||||
char *kvm; // key/value memory. Consists of repeated entries of:
|
||||
// Offset Size Description
|
||||
// 0 1 Key length, including trailing 0. (K)
|
||||
// 1 1 Value length. (V)
|
||||
// 2 1 Flags
|
||||
// 3 K Key bytes
|
||||
// 3 + K V Value bytes
|
||||
//
|
||||
// We refer to the first 3 entries as the 'tag header'.
|
||||
}; |
||||
|
||||
// Number of bytes in tag header.
|
||||
#define TAG_HEADER_SIZE 3 // key length (1) + value length (1) + flags (1)
|
||||
// Offsets to tag header entries.
|
||||
#define KEY_LEN_OFFSET 0 |
||||
#define VALUE_LEN_OFFSET 1 |
||||
#define FLAG_OFFSET 2 |
||||
|
||||
// raw_tag represents the raw-storage form of a tag in the kvm of a tag_set.
|
||||
struct raw_tag { |
||||
uint8_t key_len; |
||||
uint8_t value_len; |
||||
uint8_t flags; |
||||
char *key; |
||||
char *value; |
||||
}; |
||||
|
||||
// use reserved flag bit for indication of deleted tag.
|
||||
#define CENSUS_TAG_DELETED CENSUS_TAG_RESERVED |
||||
#define CENSUS_TAG_IS_DELETED(flags) (flags & CENSUS_TAG_DELETED) |
||||
|
||||
// Primary (external) representation of a tag set. Composed of 3 underlying
|
||||
// tag_set structs, one for each of the binary/printable propagated tags, and
|
||||
// one for everything else.
|
||||
struct census_tag_set { |
||||
struct tag_set propagated_tags; |
||||
struct tag_set propagated_binary_tags; |
||||
struct tag_set local_tags; |
||||
}; |
||||
|
||||
// Extract a raw tag given a pointer (raw) to the tag header. Allow for some
|
||||
// extra bytes in the tag header (see encode/decode for usage: allows for
|
||||
// future expansion of the tag header).
|
||||
static char *decode_tag(struct raw_tag *tag, char *header, int offset) { |
||||
tag->key_len = (uint8_t)(*header++); |
||||
tag->value_len = (uint8_t)(*header++); |
||||
tag->flags = (uint8_t)(*header++); |
||||
header += offset; |
||||
tag->key = header; |
||||
header += tag->key_len; |
||||
tag->value = header; |
||||
return header + tag->value_len; |
||||
} |
||||
|
||||
// Make a copy (in 'to') of an existing tag_set.
|
||||
static void tag_set_copy(struct tag_set *to, const struct tag_set *from) { |
||||
memcpy(to, from, sizeof(struct tag_set)); |
||||
to->kvm = gpr_malloc(to->kvm_size); |
||||
memcpy(to->kvm, from->kvm, to->kvm_used); |
||||
} |
||||
|
||||
// We may want to keep information about a deleted tag for a short time,
|
||||
// in case we can reuse the space (same tag is reinserted). This structure
|
||||
// is used for that purpose.
|
||||
struct deleted_tag_info { |
||||
struct raw_tag raw; // raw tag information.
|
||||
uint8_t *raw_flags_p; // pointer to original flags
|
||||
struct tag_set *tags; // the tag set from which tag was deleted.
|
||||
}; |
||||
|
||||
// Delete a tag from a tag set, if it exists. Returns true if the tag was
|
||||
// originally present (and is now deleted), false if it wasn't.
|
||||
static bool tag_set_delete_tag(struct tag_set *tags, |
||||
struct deleted_tag_info *dtag, const char *key, |
||||
size_t key_len) { |
||||
char *kvp = tags->kvm; |
||||
for (int i = 0; i < tags->ntags_alloc; i++) { |
||||
dtag->raw_flags_p = (uint8_t *)(kvp + FLAG_OFFSET); |
||||
kvp = decode_tag(&dtag->raw, kvp, 0); |
||||
if (CENSUS_TAG_IS_DELETED(dtag->raw.flags)) continue; |
||||
if ((key_len == dtag->raw.key_len) && |
||||
(memcmp(key, dtag->raw.key, key_len) == 0)) { |
||||
*(dtag->raw_flags_p) |= CENSUS_TAG_DELETED; |
||||
tags->ntags--; |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
// Delete a tag from a tag set. If the tag is found in any of the underlying
|
||||
// tag sets, *and* that tag set corresponds to the one in which the tag (if
|
||||
// later inserted) would be placed, then fills in dtag, and returns true.
|
||||
// Returns false otherwise. This information is later used to optimize the
|
||||
// placement of the tag if the value space can be reused, effectively
|
||||
// "undeleting" the tag.
|
||||
static bool cts_delete_tag(census_tag_set *tags, const census_tag *tag, |
||||
size_t key_len, struct deleted_tag_info *dtag) { |
||||
// use the to-be-deleted tag flags as a hint to determine the order in which
|
||||
// we delete from the underlying tag sets.
|
||||
if (CENSUS_TAG_IS_PROPAGATED(tag->flags)) { |
||||
if (CENSUS_TAG_IS_BINARY(tag->flags)) { |
||||
if (tag_set_delete_tag(&tags->propagated_binary_tags, dtag, tag->key, |
||||
key_len)) { |
||||
dtag->tags = &tags->propagated_binary_tags; |
||||
return true; |
||||
} |
||||
if (tag_set_delete_tag(&tags->propagated_tags, dtag, tag->key, key_len)) |
||||
return false; |
||||
tag_set_delete_tag(&tags->local_tags, dtag, tag->key, key_len); |
||||
} else { |
||||
if (tag_set_delete_tag(&tags->propagated_tags, dtag, tag->key, key_len)) { |
||||
dtag->tags = &tags->propagated_tags; |
||||
return true; |
||||
} |
||||
if (tag_set_delete_tag(&tags->propagated_binary_tags, dtag, tag->key, |
||||
key_len)) |
||||
return false; |
||||
tag_set_delete_tag(&tags->local_tags, dtag, tag->key, key_len); |
||||
} |
||||
} else { |
||||
if (tag_set_delete_tag(&tags->local_tags, dtag, tag->key, key_len)) { |
||||
dtag->tags = &tags->local_tags; |
||||
return true; |
||||
} |
||||
if (tag_set_delete_tag(&tags->propagated_tags, dtag, tag->key, key_len)) |
||||
return false; |
||||
tag_set_delete_tag(&tags->propagated_binary_tags, dtag, tag->key, key_len); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
// Add a tag to a tag set.
|
||||
static void tag_set_add_tag(struct tag_set *tags, const census_tag *tag, |
||||
size_t key_len) { |
||||
const size_t tag_size = key_len + tag->value_len + TAG_HEADER_SIZE; |
||||
// drop tag if too many.
|
||||
if (tags->ntags == CENSUS_MAX_TAGS) { |
||||
return; |
||||
} |
||||
if (tags->kvm_used + tag_size > tags->kvm_size) { |
||||
tags->kvm_size += 2 * CENSUS_MAX_TAG_KV_LEN + TAG_HEADER_SIZE; |
||||
char *new_kvm = gpr_malloc(tags->kvm_size); |
||||
memcpy(new_kvm, tags->kvm, tags->kvm_used); |
||||
gpr_free(tags->kvm); |
||||
tags->kvm = new_kvm; |
||||
} |
||||
char *kvp = tags->kvm + tags->kvm_used; |
||||
*kvp++ = (char)key_len; |
||||
*kvp++ = (char)tag->value_len; |
||||
// ensure reserved flags are not used.
|
||||
*kvp++ = (char)(tag->flags & (CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS | |
||||
CENSUS_TAG_BINARY)); |
||||
memcpy(kvp, tag->key, key_len); |
||||
kvp += key_len; |
||||
memcpy(kvp, tag->value, tag->value_len); |
||||
tags->kvm_used += tag_size; |
||||
tags->ntags++; |
||||
tags->ntags_alloc++; |
||||
} |
||||
|
||||
// Add a tag to a census_tag_set
|
||||
static void cts_add_tag(census_tag_set *tags, const census_tag *tag, |
||||
size_t key_len) { |
||||
// first delete the tag if it is already present
|
||||
struct deleted_tag_info dtag; |
||||
bool deleted_match = cts_delete_tag(tags, tag, key_len, &dtag); |
||||
if (tag->value != NULL && tag->value_len != 0) { |
||||
if (deleted_match && tag->value_len == dtag.raw.value_len) { |
||||
// if we have a close match for tag being added to one just deleted,
|
||||
// only need to modify value and flags.
|
||||
memcpy(dtag.raw.value, tag->value, tag->value_len); |
||||
*dtag.raw_flags_p = (tag->flags & (CENSUS_TAG_PROPAGATE | |
||||
CENSUS_TAG_STATS | CENSUS_TAG_BINARY)); |
||||
dtag.tags->ntags++; |
||||
} else { |
||||
if (CENSUS_TAG_IS_PROPAGATED(tag->flags)) { |
||||
if (CENSUS_TAG_IS_BINARY(tag->flags)) { |
||||
tag_set_add_tag(&tags->propagated_binary_tags, tag, key_len); |
||||
} else { |
||||
tag_set_add_tag(&tags->propagated_tags, tag, key_len); |
||||
} |
||||
} else { |
||||
tag_set_add_tag(&tags->local_tags, tag, key_len); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
census_tag_set *census_tag_set_create(const census_tag_set *base, |
||||
const census_tag *tags, int ntags) { |
||||
census_tag_set *new_ts = gpr_malloc(sizeof(census_tag_set)); |
||||
if (base == NULL) { |
||||
memset(new_ts, 0, sizeof(census_tag_set)); |
||||
} else { |
||||
tag_set_copy(&new_ts->propagated_tags, &base->propagated_tags); |
||||
tag_set_copy(&new_ts->propagated_binary_tags, |
||||
&base->propagated_binary_tags); |
||||
tag_set_copy(&new_ts->local_tags, &base->local_tags); |
||||
} |
||||
for (int i = 0; i < ntags; i++) { |
||||
const census_tag *tag = &tags[i]; |
||||
size_t key_len = strlen(tag->key) + 1; |
||||
// ignore the tag if it is too long/short.
|
||||
if (key_len != 1 && key_len <= CENSUS_MAX_TAG_KV_LEN && |
||||
tag->value_len <= CENSUS_MAX_TAG_KV_LEN) { |
||||
cts_add_tag(new_ts, tag, key_len); |
||||
} |
||||
} |
||||
return new_ts; |
||||
} |
||||
|
||||
void census_tag_set_destroy(census_tag_set *tags) { |
||||
gpr_free(tags->propagated_tags.kvm); |
||||
gpr_free(tags->propagated_binary_tags.kvm); |
||||
gpr_free(tags->local_tags.kvm); |
||||
gpr_free(tags); |
||||
} |
||||
|
||||
int census_tag_set_ntags(const census_tag_set *tags) { |
||||
return tags->propagated_tags.ntags + tags->propagated_binary_tags.ntags + |
||||
tags->local_tags.ntags; |
||||
} |
||||
|
||||
// Get the nth tag in a tag set. The caller must validate that index is
|
||||
// in range.
|
||||
static void tag_set_get_tag_by_index(const struct tag_set *tags, int index, |
||||
census_tag *tag) { |
||||
GPR_ASSERT(index < tags->ntags); |
||||
char *kvp = tags->kvm; |
||||
for (;;) { |
||||
struct raw_tag raw; |
||||
kvp = decode_tag(&raw, kvp, 0); |
||||
if (CENSUS_TAG_IS_DELETED(raw.flags)) { |
||||
continue; |
||||
} else if (index == 0) { |
||||
tag->key = raw.key; |
||||
tag->value = raw.value; |
||||
tag->value_len = raw.value_len; |
||||
tag->flags = raw.flags; |
||||
return; |
||||
} |
||||
index--; |
||||
} |
||||
// NOT REACHED
|
||||
} |
||||
|
||||
int census_tag_set_get_tag_by_index(const census_tag_set *tags, int index, |
||||
census_tag *tag) { |
||||
if (index < 0) return 0; |
||||
if (index < tags->propagated_tags.ntags) { |
||||
tag_set_get_tag_by_index(&tags->propagated_tags, index, tag); |
||||
return 1; |
||||
} |
||||
index -= tags->propagated_tags.ntags; |
||||
if (index < tags->propagated_binary_tags.ntags) { |
||||
tag_set_get_tag_by_index(&tags->propagated_binary_tags, index, tag); |
||||
return 1; |
||||
} |
||||
index -= tags->propagated_binary_tags.ntags; |
||||
if (index < tags->local_tags.ntags) { |
||||
tag_set_get_tag_by_index(&tags->local_tags, index, tag); |
||||
return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
// Find a tag in a tag_set by key. Return true if found, false otherwise.
|
||||
static bool tag_set_get_tag_by_key(const struct tag_set *tags, const char *key, |
||||
size_t key_len, census_tag *tag) { |
||||
char *kvp = tags->kvm; |
||||
for (int i = 0; i < tags->ntags; i++) { |
||||
struct raw_tag raw; |
||||
do { |
||||
kvp = decode_tag(&raw, kvp, 0); |
||||
} while (CENSUS_TAG_IS_DELETED(raw.flags)); |
||||
if (key_len == raw.key_len && memcmp(raw.key, key, key_len) == 0) { |
||||
tag->key = raw.key; |
||||
tag->value = raw.value; |
||||
tag->value_len = raw.value_len; |
||||
tag->flags = raw.flags; |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
int census_tag_set_get_tag_by_key(const census_tag_set *tags, const char *key, |
||||
census_tag *tag) { |
||||
size_t key_len = strlen(key) + 1; |
||||
if (key_len == 1) { |
||||
return 0; |
||||
} |
||||
if (tag_set_get_tag_by_key(&tags->propagated_tags, key, key_len, tag) || |
||||
tag_set_get_tag_by_key(&tags->propagated_binary_tags, key, key_len, |
||||
tag) || |
||||
tag_set_get_tag_by_key(&tags->local_tags, key, key_len, tag)) { |
||||
return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
// tag_set encoding and decoding functions.
|
||||
//
|
||||
// Wire format for tag sets on the wire:
|
||||
//
|
||||
// First, a tag set header:
|
||||
//
|
||||
// offset bytes description
|
||||
// 0 1 version number
|
||||
// 1 1 number of bytes in this header. This allows for future
|
||||
// expansion.
|
||||
// 2 1 number of bytes in each tag header.
|
||||
// 3 1 ntags value from tag set.
|
||||
// 4 1 ntags_alloc value from tag set.
|
||||
//
|
||||
// This is followed by the key/value memory from struct tag_set.
|
||||
|
||||
#define ENCODED_VERSION 0 // Version number
|
||||
#define ENCODED_HEADER_SIZE 5 // size of tag set header
|
||||
|
||||
// Pack a tag set into as few bytes as possible (eliding deleted tags). Assumes
|
||||
// header is already generated.
|
||||
static size_t tag_set_encode_packed(const struct tag_set *tags, char *buffer, |
||||
size_t buf_size) { |
||||
size_t encoded_size = 0; |
||||
char *kvp = tags->kvm; |
||||
for (int i = 0; i < tags->ntags_alloc; i++) { |
||||
struct raw_tag raw; |
||||
char *base = kvp; |
||||
kvp = decode_tag(&raw, kvp, 0); |
||||
size_t tag_size = |
||||
TAG_HEADER_SIZE + (size_t)raw.key_len + (size_t)raw.value_len; |
||||
if (!(CENSUS_TAG_IS_DELETED(raw.flags))) { |
||||
if (tag_size > buf_size) { |
||||
return 0; |
||||
} |
||||
memcpy(buffer, base, tag_size); |
||||
buffer += tag_size; |
||||
encoded_size += tag_size; |
||||
buf_size -= tag_size; |
||||
} |
||||
} |
||||
return encoded_size; |
||||
} |
||||
|
||||
// Encode a tag set. Returns 0 if buffer is too small.
|
||||
static size_t tag_set_encode(const struct tag_set *tags, char *buffer, |
||||
size_t buf_size) { |
||||
if (buf_size < ENCODED_HEADER_SIZE) { |
||||
return 0; |
||||
} |
||||
buf_size -= ENCODED_HEADER_SIZE; |
||||
*buffer++ = (char)ENCODED_VERSION; |
||||
*buffer++ = (char)ENCODED_HEADER_SIZE; |
||||
*buffer++ = (char)TAG_HEADER_SIZE; |
||||
*buffer++ = (char)tags->ntags; |
||||
if (tags->ntags == 0) { |
||||
*buffer = (char)tags->ntags; |
||||
return ENCODED_HEADER_SIZE; |
||||
} |
||||
if (buf_size < tags->kvm_used || tags->ntags_alloc > CENSUS_MAX_TAGS) { |
||||
*buffer++ = (char)tags->ntags; |
||||
size_t enc_size = tag_set_encode_packed(tags, buffer, buf_size); |
||||
if (enc_size == 0) { |
||||
return 0; |
||||
} |
||||
return ENCODED_HEADER_SIZE + enc_size; |
||||
} |
||||
*buffer++ = (char)tags->ntags_alloc; |
||||
memcpy(buffer, tags->kvm, tags->kvm_used); |
||||
return ENCODED_HEADER_SIZE + tags->kvm_used; |
||||
} |
||||
|
||||
size_t census_tag_set_encode_propagated(const census_tag_set *tags, |
||||
char *buffer, size_t buf_size) { |
||||
return tag_set_encode(&tags->propagated_tags, buffer, buf_size); |
||||
} |
||||
|
||||
size_t census_tag_set_encode_propagated_binary(const census_tag_set *tags, |
||||
char *buffer, size_t buf_size) { |
||||
return tag_set_encode(&tags->propagated_binary_tags, buffer, buf_size); |
||||
} |
||||
|
||||
// Decode a tag set.
|
||||
static void tag_set_decode(struct tag_set *tags, const char *buffer, |
||||
size_t size) { |
||||
uint8_t version = (uint8_t)(*buffer++); |
||||
uint8_t header_size = (uint8_t)(*buffer++); |
||||
uint8_t tag_header_size = (uint8_t)(*buffer++); |
||||
tags->ntags = (int)(*buffer++); |
||||
if (tags->ntags == 0) { |
||||
tags->ntags_alloc = 0; |
||||
tags->kvm_size = 0; |
||||
tags->kvm_used = 0; |
||||
tags->kvm = NULL; |
||||
return; |
||||
} |
||||
tags->ntags_alloc = (uint8_t)(*buffer++); |
||||
if (header_size != ENCODED_HEADER_SIZE) { |
||||
GPR_ASSERT(version != ENCODED_VERSION); |
||||
GPR_ASSERT(ENCODED_HEADER_SIZE < header_size); |
||||
buffer += (header_size - ENCODED_HEADER_SIZE); |
||||
} |
||||
tags->kvm_used = size - header_size; |
||||
tags->kvm_size = tags->kvm_used + CENSUS_MAX_TAG_KV_LEN; |
||||
tags->kvm = gpr_malloc(tags->kvm_size); |
||||
if (tag_header_size != TAG_HEADER_SIZE) { |
||||
// something new in the tag information. I don't understand it, so
|
||||
// don't copy it over.
|
||||
GPR_ASSERT(version != ENCODED_VERSION); |
||||
GPR_ASSERT(tag_header_size > TAG_HEADER_SIZE); |
||||
char *kvp = tags->kvm; |
||||
for (int i = 0; i < tags->ntags_alloc; i++) { |
||||
memcpy(kvp, buffer, TAG_HEADER_SIZE); |
||||
kvp += header_size; |
||||
struct raw_tag raw; |
||||
buffer = |
||||
decode_tag(&raw, (char *)buffer, tag_header_size - TAG_HEADER_SIZE); |
||||
memcpy(kvp, raw.key, (size_t)raw.key_len + raw.value_len); |
||||
kvp += raw.key_len + raw.value_len; |
||||
} |
||||
} else { |
||||
memcpy(tags->kvm, buffer, tags->kvm_used); |
||||
} |
||||
} |
||||
|
||||
census_tag_set *census_tag_set_decode(const char *buffer, size_t size, |
||||
const char *bin_buffer, size_t bin_size) { |
||||
census_tag_set *new_ts = gpr_malloc(sizeof(census_tag_set)); |
||||
memset(&new_ts->local_tags, 0, sizeof(struct tag_set)); |
||||
if (buffer == NULL) { |
||||
memset(&new_ts->propagated_tags, 0, sizeof(struct tag_set)); |
||||
} else { |
||||
tag_set_decode(&new_ts->propagated_tags, buffer, size); |
||||
} |
||||
if (bin_buffer == NULL) { |
||||
memset(&new_ts->propagated_binary_tags, 0, sizeof(struct tag_set)); |
||||
} else { |
||||
tag_set_decode(&new_ts->propagated_binary_tags, bin_buffer, bin_size); |
||||
} |
||||
// TODO(aveitch): check that BINARY flag is correct for each type.
|
||||
return new_ts; |
||||
} |
@ -0,0 +1,57 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* 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 Inc. 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 THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_INTERNAL_CORE_CENSUS_TAG_SET_H |
||||
#define GRPC_INTERNAL_CORE_CENSUS_TAG_SET_H |
||||
|
||||
#include <grpc/census.h> |
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpc/support/sync.h> |
||||
|
||||
/* Encode to-be-propagated tags from a tag set into a memory buffer. The total
|
||||
number of bytes used in the buffer is returned. If the buffer is too small |
||||
to contain the encoded tag set, then 0 is returned. */ |
||||
size_t census_tag_set_encode_propagated(const census_tag_set *tags, |
||||
char *buffer, size_t buf_size); |
||||
|
||||
/* Encode to-be-propagated binary tags from a tag set into a memory
|
||||
buffer. The total number of bytes used in the buffer is returned. If the |
||||
buffer is too small to contain the encoded tag set, then 0 is returned. */ |
||||
size_t census_tag_set_encode_propagated_binary(const census_tag_set *tags, |
||||
char *buffer, size_t buf_size); |
||||
|
||||
/* Decode tag set buffers encoded with census_tag_set_encode_*(). */ |
||||
census_tag_set *census_tag_set_decode(const char *buffer, size_t size, |
||||
const char *bin_buffer, size_t bin_size); |
||||
|
||||
#endif /* GRPC_INTERNAL_CORE_CENSUS_TAG_SET_H */ |
@ -0,0 +1,404 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015-2016, Google Inc. |
||||
* 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 Inc. 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 THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS 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. |
||||
* |
||||
*/ |
||||
|
||||
// Test census_tag_set functions, including encoding/decoding
|
||||
|
||||
#include "src/core/census/tag_set.h" |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/time.h> |
||||
#include <stdbool.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
static uint8_t one_byte_val = 7; |
||||
static uint32_t four_byte_val = 0x12345678; |
||||
static uint64_t eight_byte_val = 0x1234567890abcdef; |
||||
|
||||
// A set of tags Used to create a basic tag_set for testing. Each tag has a
|
||||
// unique set of flags. Note that replace_add_delete_test() relies on specific
|
||||
// offsets into this array - if you add or delete entries, you will also need
|
||||
// to change the test.
|
||||
#define BASIC_TAG_COUNT 8 |
||||
static census_tag basic_tags[BASIC_TAG_COUNT] = { |
||||
/* 0 */ {"key0", "printable", 10, 0}, |
||||
/* 1 */ {"k1", "a", 2, CENSUS_TAG_PROPAGATE}, |
||||
/* 2 */ {"k2", "longer printable string", 24, CENSUS_TAG_STATS}, |
||||
/* 3 */ {"key_three", (char *)&one_byte_val, 1, CENSUS_TAG_BINARY}, |
||||
/* 4 */ {"really_long_key_4", "random", 7, |
||||
CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS}, |
||||
/* 5 */ {"k5", (char *)&four_byte_val, 4, |
||||
CENSUS_TAG_PROPAGATE | CENSUS_TAG_BINARY}, |
||||
/* 6 */ {"k6", (char *)&eight_byte_val, 8, |
||||
CENSUS_TAG_STATS | CENSUS_TAG_BINARY}, |
||||
/* 7 */ {"k7", (char *)&four_byte_val, 4, |
||||
CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS | CENSUS_TAG_BINARY}}; |
||||
|
||||
// Set of tags used to modify the basic tag_set. Note that
|
||||
// replace_add_delete_test() relies on specific offsets into this array - if
|
||||
// you add or delete entries, you will also need to change the test. Other
|
||||
// tests that rely on specific instances have XXX_XXX_OFFSET definitions (also
|
||||
// change the defines below if you add/delete entires).
|
||||
#define MODIFY_TAG_COUNT 10 |
||||
static census_tag modify_tags[MODIFY_TAG_COUNT] = { |
||||
#define REPLACE_VALUE_OFFSET 0 |
||||
/* 0 */ {"key0", "replace printable", 18, 0}, // replaces tag value only
|
||||
#define ADD_TAG_OFFSET 1 |
||||
/* 1 */ {"new_key", "xyzzy", 6, CENSUS_TAG_STATS}, // new tag
|
||||
#define DELETE_TAG_OFFSET 2 |
||||
/* 2 */ {"k5", NULL, 5, |
||||
0}, // should delete tag, despite bogus value length
|
||||
/* 3 */ {"k6", "foo", 0, 0}, // should delete tag, despite bogus value
|
||||
/* 4 */ {"k6", "foo", 0, 0}, // try deleting already-deleted tag
|
||||
/* 5 */ {"non-existent", NULL, 0, 0}, // another non-existent tag
|
||||
#define REPLACE_FLAG_OFFSET 6 |
||||
/* 6 */ {"k1", "a", 2, 0}, // change flags only
|
||||
/* 7 */ {"k7", "bar", 4, CENSUS_TAG_STATS}, // change flags and value
|
||||
/* 8 */ {"k2", (char *)&eight_byte_val, 8, |
||||
CENSUS_TAG_BINARY | CENSUS_TAG_PROPAGATE}, // more flags change
|
||||
// non-binary -> binary
|
||||
/* 9 */ {"k6", "bar", 4, |
||||
0} // add back tag, with different value, but same length
|
||||
}; |
||||
|
||||
// Utility function to compare tags. Returns true if all fields match.
|
||||
static bool compare_tag(const census_tag *t1, const census_tag *t2) { |
||||
return (strcmp(t1->key, t2->key) == 0 && t1->value_len == t2->value_len && |
||||
memcmp(t1->value, t2->value, t1->value_len) == 0 && |
||||
t1->flags == t2->flags); |
||||
} |
||||
|
||||
// Utility function to validate a tag exists in tag set.
|
||||
static bool validate_tag(const census_tag_set *cts, const census_tag *tag) { |
||||
census_tag tag2; |
||||
if (census_tag_set_get_tag_by_key(cts, tag->key, &tag2) != 1) return false; |
||||
return compare_tag(tag, &tag2); |
||||
} |
||||
|
||||
// Create an empty tag_set.
|
||||
static void empty_test(void) { |
||||
struct census_tag_set *cts = census_tag_set_create(NULL, NULL, 0); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == 0); |
||||
census_tag_set_destroy(cts); |
||||
} |
||||
|
||||
// Create basic tag set, and test that retreiving tag by index works.
|
||||
static void basic_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
for (int i = 0; i < census_tag_set_ntags(cts); i++) { |
||||
census_tag tag; |
||||
GPR_ASSERT(census_tag_set_get_tag_by_index(cts, i, &tag) == 1); |
||||
// can't rely on tag return order: make sure it matches exactly one.
|
||||
int matches = 0; |
||||
for (int j = 0; j < BASIC_TAG_COUNT; j++) { |
||||
if (compare_tag(&tag, &basic_tags[j])) matches++; |
||||
} |
||||
GPR_ASSERT(matches == 1); |
||||
} |
||||
census_tag_set_destroy(cts); |
||||
} |
||||
|
||||
// Try census_tag_set_get_tag_by_index() with bad indices.
|
||||
static void bad_index_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_tag_set_get_tag_by_index(cts, -1, &tag) == 0); |
||||
GPR_ASSERT(census_tag_set_get_tag_by_index(cts, BASIC_TAG_COUNT, &tag) == 0); |
||||
GPR_ASSERT(census_tag_set_get_tag_by_index(cts, BASIC_TAG_COUNT + 1, &tag) == |
||||
0); |
||||
census_tag_set_destroy(cts); |
||||
} |
||||
|
||||
// Test that census_tag_set_get_tag_by_key().
|
||||
static void lookup_by_key_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
census_tag tag; |
||||
for (int i = 0; i < census_tag_set_ntags(cts); i++) { |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts, basic_tags[i].key, &tag) == |
||||
1); |
||||
GPR_ASSERT(compare_tag(&tag, &basic_tags[i])); |
||||
} |
||||
// non-existent keys
|
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts, "key", &tag) == 0); |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts, "key01", &tag) == 0); |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts, "k9", &tag) == 0); |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts, "random", &tag) == 0); |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts, "", &tag) == 0); |
||||
census_tag_set_destroy(cts); |
||||
} |
||||
|
||||
// Try creating tag set with invalid entries.
|
||||
static void invalid_test(void) { |
||||
char key[300]; |
||||
memset(key, 'k', 299); |
||||
key[299] = 0; |
||||
char value[300]; |
||||
memset(value, 'v', 300); |
||||
census_tag tag = {key, value, 3, CENSUS_TAG_BINARY}; |
||||
// long keys, short value. Key lengths (including terminator) should be
|
||||
// <= 255 (CENSUS_MAX_TAG_KV_LEN)
|
||||
GPR_ASSERT(strlen(key) == 299); |
||||
struct census_tag_set *cts = census_tag_set_create(NULL, &tag, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == 0); |
||||
census_tag_set_destroy(cts); |
||||
key[CENSUS_MAX_TAG_KV_LEN] = 0; |
||||
GPR_ASSERT(strlen(key) == CENSUS_MAX_TAG_KV_LEN); |
||||
cts = census_tag_set_create(NULL, &tag, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == 0); |
||||
census_tag_set_destroy(cts); |
||||
key[CENSUS_MAX_TAG_KV_LEN - 1] = 0; |
||||
GPR_ASSERT(strlen(key) == CENSUS_MAX_TAG_KV_LEN - 1); |
||||
cts = census_tag_set_create(NULL, &tag, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == 1); |
||||
census_tag_set_destroy(cts); |
||||
// now try with long values
|
||||
tag.value_len = 300; |
||||
cts = census_tag_set_create(NULL, &tag, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == 0); |
||||
census_tag_set_destroy(cts); |
||||
tag.value_len = CENSUS_MAX_TAG_KV_LEN + 1; |
||||
cts = census_tag_set_create(NULL, &tag, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == 0); |
||||
census_tag_set_destroy(cts); |
||||
tag.value_len = CENSUS_MAX_TAG_KV_LEN; |
||||
cts = census_tag_set_create(NULL, &tag, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == 1); |
||||
census_tag_set_destroy(cts); |
||||
// 0 length key.
|
||||
key[0] = 0; |
||||
cts = census_tag_set_create(NULL, &tag, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == 0); |
||||
census_tag_set_destroy(cts); |
||||
} |
||||
|
||||
// Make a copy of a tag set
|
||||
static void copy_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
struct census_tag_set *cts2 = census_tag_set_create(cts, NULL, 0); |
||||
GPR_ASSERT(census_tag_set_ntags(cts2) == BASIC_TAG_COUNT); |
||||
for (int i = 0; i < census_tag_set_ntags(cts2); i++) { |
||||
census_tag tag; |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts2, basic_tags[i].key, &tag) == |
||||
1); |
||||
GPR_ASSERT(compare_tag(&tag, &basic_tags[i])); |
||||
} |
||||
census_tag_set_destroy(cts); |
||||
census_tag_set_destroy(cts2); |
||||
} |
||||
|
||||
// replace a single tag value
|
||||
static void replace_value_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
struct census_tag_set *cts2 = |
||||
census_tag_set_create(cts, modify_tags + REPLACE_VALUE_OFFSET, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts2) == BASIC_TAG_COUNT); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key( |
||||
cts2, modify_tags[REPLACE_VALUE_OFFSET].key, &tag) == 1); |
||||
GPR_ASSERT(compare_tag(&tag, &modify_tags[REPLACE_VALUE_OFFSET])); |
||||
census_tag_set_destroy(cts); |
||||
census_tag_set_destroy(cts2); |
||||
} |
||||
|
||||
// replace a single tags flags
|
||||
static void replace_flags_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
struct census_tag_set *cts2 = |
||||
census_tag_set_create(cts, modify_tags + REPLACE_FLAG_OFFSET, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts2) == BASIC_TAG_COUNT); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key( |
||||
cts2, modify_tags[REPLACE_FLAG_OFFSET].key, &tag) == 1); |
||||
GPR_ASSERT(compare_tag(&tag, &modify_tags[REPLACE_FLAG_OFFSET])); |
||||
census_tag_set_destroy(cts); |
||||
census_tag_set_destroy(cts2); |
||||
} |
||||
|
||||
// delete a single tag.
|
||||
static void delete_tag_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
struct census_tag_set *cts2 = |
||||
census_tag_set_create(cts, modify_tags + DELETE_TAG_OFFSET, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts2) == BASIC_TAG_COUNT - 1); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key( |
||||
cts2, modify_tags[DELETE_TAG_OFFSET].key, &tag) == 0); |
||||
census_tag_set_destroy(cts); |
||||
census_tag_set_destroy(cts2); |
||||
} |
||||
|
||||
// add a single new tag.
|
||||
static void add_tag_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
struct census_tag_set *cts2 = |
||||
census_tag_set_create(cts, modify_tags + ADD_TAG_OFFSET, 1); |
||||
GPR_ASSERT(census_tag_set_ntags(cts2) == BASIC_TAG_COUNT + 1); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key( |
||||
cts2, modify_tags[ADD_TAG_OFFSET].key, &tag) == 1); |
||||
GPR_ASSERT(compare_tag(&tag, &modify_tags[ADD_TAG_OFFSET])); |
||||
census_tag_set_destroy(cts); |
||||
census_tag_set_destroy(cts2); |
||||
} |
||||
|
||||
// test many changes at once.
|
||||
static void replace_add_delete_test(void) { |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
struct census_tag_set *cts2 = |
||||
census_tag_set_create(cts, modify_tags, MODIFY_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts2) == 8); |
||||
// validate tag set contents. Use specific indices into the two arrays
|
||||
// holding tag values.
|
||||
GPR_ASSERT(validate_tag(cts2, &basic_tags[3])); |
||||
GPR_ASSERT(validate_tag(cts2, &basic_tags[4])); |
||||
GPR_ASSERT(validate_tag(cts2, &modify_tags[0])); |
||||
GPR_ASSERT(validate_tag(cts2, &modify_tags[1])); |
||||
GPR_ASSERT(validate_tag(cts2, &modify_tags[6])); |
||||
GPR_ASSERT(validate_tag(cts2, &modify_tags[7])); |
||||
GPR_ASSERT(validate_tag(cts2, &modify_tags[8])); |
||||
GPR_ASSERT(validate_tag(cts2, &modify_tags[9])); |
||||
GPR_ASSERT(!validate_tag(cts2, &basic_tags[0])); |
||||
GPR_ASSERT(!validate_tag(cts2, &basic_tags[1])); |
||||
GPR_ASSERT(!validate_tag(cts2, &basic_tags[2])); |
||||
GPR_ASSERT(!validate_tag(cts2, &basic_tags[5])); |
||||
GPR_ASSERT(!validate_tag(cts2, &basic_tags[6])); |
||||
GPR_ASSERT(!validate_tag(cts2, &basic_tags[7])); |
||||
census_tag_set_destroy(cts); |
||||
census_tag_set_destroy(cts2); |
||||
} |
||||
|
||||
// Use the basic tag set to test encode/decode.
|
||||
static void simple_encode_decode_test(void) { |
||||
char buf1[1000]; |
||||
char buf2[1000]; |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_encode_propagated(cts, buf1, 1) == 0); |
||||
size_t b1 = census_tag_set_encode_propagated(cts, buf1, 1000); |
||||
GPR_ASSERT(b1 != 0); |
||||
GPR_ASSERT(census_tag_set_encode_propagated_binary(cts, buf2, 1) == 0); |
||||
size_t b2 = census_tag_set_encode_propagated_binary(cts, buf2, 1000); |
||||
GPR_ASSERT(b2 != 0); |
||||
census_tag_set *cts2 = census_tag_set_decode(buf1, b1, buf2, b2); |
||||
GPR_ASSERT(cts2 != NULL); |
||||
GPR_ASSERT(census_tag_set_ntags(cts2) == 4); |
||||
for (int i = 0; i < census_tag_set_ntags(cts); i++) { |
||||
census_tag tag; |
||||
if (CENSUS_TAG_IS_PROPAGATED(basic_tags[i].flags)) { |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts2, basic_tags[i].key, &tag) == |
||||
1); |
||||
GPR_ASSERT(compare_tag(&tag, &basic_tags[i])); |
||||
} else { |
||||
GPR_ASSERT(census_tag_set_get_tag_by_key(cts2, basic_tags[i].key, &tag) == |
||||
0); |
||||
} |
||||
} |
||||
census_tag_set_destroy(cts2); |
||||
census_tag_set_destroy(cts); |
||||
} |
||||
|
||||
// Use more complex/modified tag set to test encode/decode.
|
||||
static void complex_encode_decode_test(void) { |
||||
char buf1[500]; |
||||
char buf2[500]; |
||||
struct census_tag_set *cts = |
||||
census_tag_set_create(NULL, basic_tags, BASIC_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts) == BASIC_TAG_COUNT); |
||||
struct census_tag_set *cts2 = |
||||
census_tag_set_create(cts, modify_tags, MODIFY_TAG_COUNT); |
||||
GPR_ASSERT(census_tag_set_ntags(cts2) == 8); |
||||
|
||||
size_t b1 = census_tag_set_encode_propagated(cts2, buf1, 500); |
||||
GPR_ASSERT(b1 != 0); |
||||
size_t b2 = census_tag_set_encode_propagated_binary(cts2, buf2, 500); |
||||
GPR_ASSERT(b2 != 0); |
||||
census_tag_set *cts3 = census_tag_set_decode(buf1, b1, buf2, b2); |
||||
GPR_ASSERT(cts3 != NULL); |
||||
GPR_ASSERT(census_tag_set_ntags(cts3) == 2); |
||||
GPR_ASSERT(validate_tag(cts3, &basic_tags[4])); |
||||
GPR_ASSERT(validate_tag(cts3, &modify_tags[8])); |
||||
// Now force tag set to be in smaller space
|
||||
census_tag_set_destroy(cts3); |
||||
size_t nb1 = census_tag_set_encode_propagated(cts2, buf1, b1 - 1); |
||||
GPR_ASSERT(nb1 != 0); |
||||
GPR_ASSERT(nb1 < b1); |
||||
size_t nb2 = census_tag_set_encode_propagated_binary(cts2, buf2, b2 - 1); |
||||
GPR_ASSERT(nb2 != 0); |
||||
GPR_ASSERT(nb2 < b2); |
||||
cts3 = census_tag_set_decode(buf1, nb1, buf2, nb2); |
||||
GPR_ASSERT(cts3 != NULL); |
||||
GPR_ASSERT(census_tag_set_ntags(cts3) == 2); |
||||
GPR_ASSERT(validate_tag(cts3, &basic_tags[4])); |
||||
GPR_ASSERT(validate_tag(cts3, &modify_tags[8])); |
||||
census_tag_set_destroy(cts3); |
||||
census_tag_set_destroy(cts2); |
||||
census_tag_set_destroy(cts); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
grpc_test_init(argc, argv); |
||||
empty_test(); |
||||
basic_test(); |
||||
bad_index_test(); |
||||
lookup_by_key_test(); |
||||
invalid_test(); |
||||
copy_test(); |
||||
replace_value_test(); |
||||
replace_flags_test(); |
||||
delete_tag_test(); |
||||
add_tag_test(); |
||||
replace_add_delete_test(); |
||||
simple_encode_decode_test(); |
||||
complex_encode_decode_test(); |
||||
return 0; |
||||
} |
@ -0,0 +1,197 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\1.0.204.1.props')" /> |
||||
<ItemGroup Label="ProjectConfigurations"> |
||||
<ProjectConfiguration Include="Debug|Win32"> |
||||
<Configuration>Debug</Configuration> |
||||
<Platform>Win32</Platform> |
||||
</ProjectConfiguration> |
||||
<ProjectConfiguration Include="Debug|x64"> |
||||
<Configuration>Debug</Configuration> |
||||
<Platform>x64</Platform> |
||||
</ProjectConfiguration> |
||||
<ProjectConfiguration Include="Release|Win32"> |
||||
<Configuration>Release</Configuration> |
||||
<Platform>Win32</Platform> |
||||
</ProjectConfiguration> |
||||
<ProjectConfiguration Include="Release|x64"> |
||||
<Configuration>Release</Configuration> |
||||
<Platform>x64</Platform> |
||||
</ProjectConfiguration> |
||||
</ItemGroup> |
||||
<PropertyGroup Label="Globals"> |
||||
<ProjectGuid>{430F8F07-6AAD-0150-B35B-DB9E2E21941A}</ProjectGuid> |
||||
<IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected> |
||||
<IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir> |
||||
</PropertyGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> |
||||
<PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration"> |
||||
<PlatformToolset>v100</PlatformToolset> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration"> |
||||
<PlatformToolset>v110</PlatformToolset> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration"> |
||||
<PlatformToolset>v120</PlatformToolset> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration"> |
||||
<PlatformToolset>v140</PlatformToolset> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> |
||||
<ConfigurationType>Application</ConfigurationType> |
||||
<UseDebugLibraries>true</UseDebugLibraries> |
||||
<CharacterSet>Unicode</CharacterSet> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> |
||||
<ConfigurationType>Application</ConfigurationType> |
||||
<UseDebugLibraries>false</UseDebugLibraries> |
||||
<WholeProgramOptimization>true</WholeProgramOptimization> |
||||
<CharacterSet>Unicode</CharacterSet> |
||||
</PropertyGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> |
||||
<ImportGroup Label="ExtensionSettings"> |
||||
</ImportGroup> |
||||
<ImportGroup Label="PropertySheets"> |
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\global.props" /> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\openssl.props" /> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\winsock.props" /> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\zlib.props" /> |
||||
</ImportGroup> |
||||
<PropertyGroup Label="UserMacros" /> |
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'"> |
||||
<TargetName>tag_set_test</TargetName> |
||||
<Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib> |
||||
<Configuration-grpc_dependencies_zlib>Debug</Configuration-grpc_dependencies_zlib> |
||||
<Configuration-grpc_dependencies_openssl>Debug</Configuration-grpc_dependencies_openssl> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'"> |
||||
<TargetName>tag_set_test</TargetName> |
||||
<Linkage-grpc_dependencies_zlib>static</Linkage-grpc_dependencies_zlib> |
||||
<Configuration-grpc_dependencies_zlib>Release</Configuration-grpc_dependencies_zlib> |
||||
<Configuration-grpc_dependencies_openssl>Release</Configuration-grpc_dependencies_openssl> |
||||
</PropertyGroup> |
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |
||||
<ClCompile> |
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader> |
||||
<WarningLevel>Level3</WarningLevel> |
||||
<Optimization>Disabled</Optimization> |
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
<SDLCheck>true</SDLCheck> |
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> |
||||
<TreatWarningAsError>true</TreatWarningAsError> |
||||
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> |
||||
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> |
||||
</ClCompile> |
||||
<Link> |
||||
<SubSystem>Console</SubSystem> |
||||
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> |
||||
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> |
||||
</Link> |
||||
</ItemDefinitionGroup> |
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> |
||||
<ClCompile> |
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader> |
||||
<WarningLevel>Level3</WarningLevel> |
||||
<Optimization>Disabled</Optimization> |
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
<SDLCheck>true</SDLCheck> |
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> |
||||
<TreatWarningAsError>true</TreatWarningAsError> |
||||
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> |
||||
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> |
||||
</ClCompile> |
||||
<Link> |
||||
<SubSystem>Console</SubSystem> |
||||
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> |
||||
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> |
||||
</Link> |
||||
</ItemDefinitionGroup> |
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |
||||
<ClCompile> |
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader> |
||||
<WarningLevel>Level3</WarningLevel> |
||||
<Optimization>MaxSpeed</Optimization> |
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
<FunctionLevelLinking>true</FunctionLevelLinking> |
||||
<IntrinsicFunctions>true</IntrinsicFunctions> |
||||
<SDLCheck>true</SDLCheck> |
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> |
||||
<TreatWarningAsError>true</TreatWarningAsError> |
||||
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> |
||||
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> |
||||
</ClCompile> |
||||
<Link> |
||||
<SubSystem>Console</SubSystem> |
||||
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> |
||||
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> |
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding> |
||||
<OptimizeReferences>true</OptimizeReferences> |
||||
</Link> |
||||
</ItemDefinitionGroup> |
||||
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> |
||||
<ClCompile> |
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader> |
||||
<WarningLevel>Level3</WarningLevel> |
||||
<Optimization>MaxSpeed</Optimization> |
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
<FunctionLevelLinking>true</FunctionLevelLinking> |
||||
<IntrinsicFunctions>true</IntrinsicFunctions> |
||||
<SDLCheck>true</SDLCheck> |
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> |
||||
<TreatWarningAsError>true</TreatWarningAsError> |
||||
<DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat> |
||||
<MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild> |
||||
</ClCompile> |
||||
<Link> |
||||
<SubSystem>Console</SubSystem> |
||||
<GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation> |
||||
<GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation> |
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding> |
||||
<OptimizeReferences>true</OptimizeReferences> |
||||
</Link> |
||||
</ItemDefinitionGroup> |
||||
|
||||
<ItemGroup> |
||||
<ClCompile Include="$(SolutionDir)\..\test\core\census\tag_set_test.c"> |
||||
</ClCompile> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj"> |
||||
<Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project> |
||||
</ProjectReference> |
||||
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj"> |
||||
<Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project> |
||||
</ProjectReference> |
||||
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj"> |
||||
<Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project> |
||||
</ProjectReference> |
||||
<ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj"> |
||||
<Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project> |
||||
</ProjectReference> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<None Include="packages.config" /> |
||||
</ItemGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |
||||
<ImportGroup Label="ExtensionTargets"> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" /> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies\grpc.dependencies.zlib.targets')" /> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" /> |
||||
<Import Project="$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets" Condition="Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies\grpc.dependencies.openssl.targets')" /> |
||||
</ImportGroup> |
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> |
||||
<PropertyGroup> |
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> |
||||
</PropertyGroup> |
||||
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\native\grpc.dependencies.zlib.redist.targets')" /> |
||||
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.zlib.1.2.8.10\build\native\grpc.dependencies.zlib.targets')" /> |
||||
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\native\grpc.dependencies.openssl.redist.targets')" /> |
||||
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.props')" /> |
||||
<Error Condition="!Exists('$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\..\vsprojects\packages\grpc.dependencies.openssl.1.0.204.1\build\native\grpc.dependencies.openssl.targets')" /> |
||||
</Target> |
||||
</Project> |
||||
|
@ -0,0 +1,21 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
||||
<ClCompile Include="$(SolutionDir)\..\test\core\census\tag_set_test.c"> |
||||
<Filter>test\core\census</Filter> |
||||
</ClCompile> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<Filter Include="test"> |
||||
<UniqueIdentifier>{500aa440-5924-8047-996a-4c5096d1ef96}</UniqueIdentifier> |
||||
</Filter> |
||||
<Filter Include="test\core"> |
||||
<UniqueIdentifier>{a3bf80f0-5b13-f623-277b-05f0231dd933}</UniqueIdentifier> |
||||
</Filter> |
||||
<Filter Include="test\core\census"> |
||||
<UniqueIdentifier>{b6ed1b86-7795-4da9-a169-9eccf836852c}</UniqueIdentifier> |
||||
</Filter> |
||||
</ItemGroup> |
||||
</Project> |
||||
|
Loading…
Reference in new issue