[PATCH v2 4/5] box: introduce field_map_builder for field_map init
Kirill Shcherbatov
kshcherbatov at tarantool.org
Tue Mar 19 15:32:09 MSK 2019
The new field_map_builder class encapsulates the logic associated with
field_map allocation and initialization. In the future it will
be extended to allocate field_map that has extentions.
Needed for #1257
---
src/box/CMakeLists.txt | 1 +
src/box/memtx_engine.c | 8 +-
src/box/tuple.c | 16 ++--
src/box/tuple_field_map.c | 53 ++++++++++++
src/box/tuple_field_map.h | 86 ++++++++++++++++++
src/box/tuple_format.c | 178 +++++++++++++++++++-------------------
src/box/tuple_format.h | 7 +-
src/box/vy_stmt.c | 9 +-
8 files changed, 252 insertions(+), 106 deletions(-)
create mode 100644 src/box/tuple_field_map.c
create mode 100644 src/box/tuple_field_map.h
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 59e91b65a..3ae47e336 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -35,6 +35,7 @@ target_link_libraries(xrow server core small vclock misc box_error
add_library(tuple STATIC
tuple.c
+ tuple_field_map.c
tuple_format.c
tuple_update.c
tuple_compare.cc
diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c
index 3e959ff2e..bcd170a89 100644
--- a/src/box/memtx_engine.c
+++ b/src/box/memtx_engine.c
@@ -1125,10 +1125,10 @@ memtx_tuple_new(struct tuple_format *format, const char *data, const char *end)
struct tuple *tuple = NULL;
struct region *region = &fiber()->gc;
size_t region_svp = region_used(region);
- uint32_t *field_map, field_map_size;
- if (tuple_field_map_create(format, data, true, &field_map,
- &field_map_size) != 0)
+ struct field_map_builder builder;
+ if (tuple_field_map_create(&builder, format, data, true) != 0)
goto end;
+ uint32_t field_map_size = field_map_builder_size(&builder);
size_t tuple_len = end - data;
size_t total = sizeof(struct memtx_tuple) + field_map_size + tuple_len;
@@ -1168,7 +1168,7 @@ memtx_tuple_new(struct tuple_format *format, const char *data, const char *end)
*/
tuple->data_offset = sizeof(struct tuple) + field_map_size;
char *raw = (char *) tuple + tuple->data_offset;
- memcpy(raw - field_map_size, field_map, field_map_size);
+ field_map_builder_build(&builder, raw - field_map_size);
memcpy(raw, data, tuple_len);
say_debug("%s(%zu) = %p", __func__, tuple_len, memtx_tuple);
end:
diff --git a/src/box/tuple.c b/src/box/tuple.c
index 7f06d4053..68f0670f2 100644
--- a/src/box/tuple.c
+++ b/src/box/tuple.c
@@ -78,10 +78,10 @@ runtime_tuple_new(struct tuple_format *format, const char *data, const char *end
struct tuple *tuple = NULL;
struct region *region = &fiber()->gc;
size_t region_svp = region_used(region);
- uint32_t *field_map, field_map_size;
- if (tuple_field_map_create(format, data, true, &field_map,
- &field_map_size) != 0)
+ struct field_map_builder builder;
+ if (tuple_field_map_create(&builder, format, data, true) != 0)
goto end;
+ uint32_t field_map_size = field_map_builder_size(&builder);
size_t data_len = end - data;
size_t total = sizeof(struct tuple) + field_map_size + data_len;
@@ -98,7 +98,7 @@ runtime_tuple_new(struct tuple_format *format, const char *data, const char *end
tuple_format_ref(format);
tuple->data_offset = sizeof(struct tuple) + field_map_size;
char *raw = (char *) tuple + tuple->data_offset;
- memcpy(raw - field_map_size, field_map, field_map_size);
+ field_map_builder_build(&builder, raw - field_map_size);
memcpy(raw, data, data_len);
say_debug("%s(%zu) = %p", __func__, data_len, tuple);
end:
@@ -126,11 +126,11 @@ tuple_validate_raw(struct tuple_format *format, const char *tuple)
struct region *region = &fiber()->gc;
size_t region_svp = region_used(region);
- uint32_t *field_map, field_map_size;
- int rc = tuple_field_map_create(format, tuple, true, &field_map,
- &field_map_size);
- assert(rc != 0 || field_map_size == format->field_map_size);
+ struct field_map_builder builder;
+ int rc = tuple_field_map_create(&builder, format, tuple, true);
region_truncate(region, region_svp);
+ assert(rc != 0 ||
+ field_map_builder_size(&builder) == format->field_map_size);
return rc;
}
diff --git a/src/box/tuple_field_map.c b/src/box/tuple_field_map.c
new file mode 100644
index 000000000..ea77745aa
--- /dev/null
+++ b/src/box/tuple_field_map.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 "diag.h"
+#include "tuple_field_map.h"
+#include "small/region.h"
+
+int
+field_map_builder_create(struct field_map_builder *builder,
+ uint32_t field_map_size, struct region *region)
+{
+ builder->item_count = field_map_size / sizeof(uint32_t);
+ if (field_map_size == 0) {
+ builder->items = NULL;
+ return 0;
+ }
+ uint32_t sz = builder->item_count * sizeof(field_map_builder_item);
+ builder->items = region_alloc(region, sz);
+ if (builder->items == NULL) {
+ diag_set(OutOfMemory, sz, "region_alloc", "field_map");
+ return -1;
+ }
+ memset((char *)builder->items, 0, sz);
+ builder->items = (field_map_builder_item *)((char *)builder->items + sz);
+ return 0;
+}
diff --git a/src/box/tuple_field_map.h b/src/box/tuple_field_map.h
new file mode 100644
index 000000000..3a9927faa
--- /dev/null
+++ b/src/box/tuple_field_map.h
@@ -0,0 +1,86 @@
+#ifndef TARANTOOL_BOX_FIELD_MAP_H_INCLUDED
+#define TARANTOOL_BOX_FIELD_MAP_H_INCLUDED
+/*
+ * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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
+ * <COPYRIGHT HOLDER> 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 <stdint.h>
+
+struct region;
+
+/** Preliminary field_map atom. */
+typedef uint32_t field_map_builder_item;
+
+/** A class that contains a preliminary view of the field_map. */
+struct field_map_builder {
+ /**
+ * Allocation with preliminary offset slot
+ * representation.
+ */
+ field_map_builder_item *items;
+ /**
+ * Number of fields in the resulting field_map;
+ * count of field_map_builder::items.
+ */
+ uint32_t item_count;
+};
+
+/**
+ * Initialize field_map_builder to prepare field_map of size
+ * field_map_size. Use region for temporary allocations.
+ */
+int
+field_map_builder_create(struct field_map_builder *builder,
+ uint32_t field_map_size, struct region *region);
+
+/** Initialize offset_slot with offset value. */
+static inline void
+field_map_builder_slot_set(struct field_map_builder *builder,
+ int32_t offset_slot, uint32_t offset)
+{
+ assert(offset_slot < 0);
+ builder->items[offset_slot] = offset;
+}
+
+/** Return built field_map size. */
+static inline uint32_t
+field_map_builder_size(struct field_map_builder *builder)
+{
+ return builder->item_count * sizeof(uint32_t);
+}
+
+/** Write build field_map size in the preallocated buffer wptr. */
+static inline void
+field_map_builder_build(struct field_map_builder *builder, char *wptr)
+{
+ uint32_t field_map_size = field_map_builder_size(builder);
+ memcpy(wptr, (char *)builder->items - field_map_size, field_map_size);
+}
+
+#endif /* TARANTOOL_BOX_FIELD_MAP_H_INCLUDED */
diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c
index 1043707ad..1d765c7c7 100644
--- a/src/box/tuple_format.c
+++ b/src/box/tuple_format.c
@@ -767,70 +767,13 @@ tuple_format1_can_store_format2_tuples(struct tuple_format *format1,
return true;
}
-/** @sa declaration for details. */
-int
-tuple_field_map_create(struct tuple_format *format, const char *tuple,
- bool validate, uint32_t **field_map,
- uint32_t *field_map_size)
+static int
+tuple_field_map_initialize(struct field_map_builder *builder,
+ struct tuple_format *format, const char **pos,
+ const char *tuple, uint32_t defined_field_count,
+ void *required_fields, struct region *region)
{
- if (tuple_format_field_count(format) == 0) {
- *field_map = NULL;
- *field_map_size = 0;
- return 0; /* Nothing to initialize */
- }
- struct region *region = &fiber()->gc;
- *field_map_size = format->field_map_size;
- *field_map = region_alloc(region, *field_map_size);
- if (*field_map == NULL) {
- diag_set(OutOfMemory, *field_map_size, "region_alloc",
- "field_map");
- return -1;
- }
- *field_map = (uint32_t *)((char *)*field_map + *field_map_size);
-
- const char *pos = tuple;
- int rc = 0;
- /* Check to see if the tuple has a sufficient number of fields. */
- uint32_t field_count = mp_decode_array(&pos);
- if (validate && format->exact_field_count > 0 &&
- format->exact_field_count != field_count) {
- diag_set(ClientError, ER_EXACT_FIELD_COUNT,
- (unsigned) field_count,
- (unsigned) format->exact_field_count);
- goto error;
- }
- /*
- * Allocate a field bitmap that will be used for checking
- * that all mandatory fields are present.
- */
- void *required_fields = NULL;
- size_t required_fields_sz = 0;
- if (validate) {
- required_fields_sz = bitmap_size(format->total_field_count);
- required_fields = region_alloc(region, required_fields_sz);
- if (required_fields == NULL) {
- diag_set(OutOfMemory, required_fields_sz,
- "region", "required field bitmap");
- goto error;
- }
- memcpy(required_fields, format->required_fields,
- required_fields_sz);
- }
- /*
- * Initialize the tuple field map and validate field types.
- */
- if (field_count == 0) {
- /* Empty tuple, nothing to do. */
- goto finish;
- }
- uint32_t defined_field_count = MIN(field_count, validate ?
- tuple_format_field_count(format) :
- format->index_field_count);
- /*
- * Nullify field map to be able to detect by 0,
- * which key fields are absent in tuple_field().
- */
- memset((char *)*field_map - *field_map_size, 0, *field_map_size);
+ bool validate = required_fields != NULL;
/*
* Prepare mp stack of the size equal to the maximum depth
* of the indexed field in the format::fields tree
@@ -842,12 +785,11 @@ tuple_field_map_create(struct tuple_format *format, const char *tuple,
struct mp_frame *frames = region_alloc(region, frames_sz);
if (frames == NULL) {
diag_set(OutOfMemory, frames_sz, "region", "frames");
- goto error;
+ return -1;
}
struct mp_stack stack;
mp_stack_create(&stack, format->fields_depth, frames);
mp_stack_push(&stack, MP_ARRAY, defined_field_count);
- struct tuple_field *field;
struct json_token *parent = &format->fields.root;
while (true) {
int idx;
@@ -862,7 +804,7 @@ tuple_field_map_create(struct tuple_format *format, const char *tuple,
*/
mp_stack_pop(&stack);
if (mp_stack_is_empty(&stack))
- goto finish;
+ goto end;
parent = parent->parent;
}
/*
@@ -877,17 +819,17 @@ tuple_field_map_create(struct tuple_format *format, const char *tuple,
token.num = idx;
break;
case MP_MAP:
- if (mp_typeof(*pos) != MP_STR) {
+ if (mp_typeof(**pos) != MP_STR) {
/*
* JSON path support only string
* keys for map. Skip this entry.
*/
- mp_next(&pos);
- mp_next(&pos);
+ mp_next(pos);
+ mp_next(pos);
continue;
}
token.type = JSON_TOKEN_STR;
- token.str = mp_decode_str(&pos, (uint32_t *)&token.len);
+ token.str = mp_decode_str(pos, (uint32_t *)&token.len);
break;
default:
unreachable();
@@ -898,9 +840,10 @@ tuple_field_map_create(struct tuple_format *format, const char *tuple,
* corresponding to the current position in the
* tuple.
*/
- enum mp_type type = mp_typeof(*pos);
+ enum mp_type type = mp_typeof(**pos);
assert(parent != NULL);
- field = json_tree_lookup_entry(&format->fields, parent, &token,
+ struct tuple_field *field =
+ json_tree_lookup_entry(&format->fields, parent, &token,
struct tuple_field, token);
if (field != NULL) {
bool is_nullable = tuple_field_is_nullable(field);
@@ -910,11 +853,14 @@ tuple_field_map_create(struct tuple_format *format, const char *tuple,
diag_set(ClientError, ER_FIELD_TYPE,
tuple_field_path(field),
field_type_strs[field->type]);
- goto error;
+ return -1;
}
- if (field->offset_slot != TUPLE_OFFSET_SLOT_NIL)
- (*field_map)[field->offset_slot] = pos - tuple;
- if (required_fields != NULL)
+ if (field->offset_slot != TUPLE_OFFSET_SLOT_NIL) {
+ field_map_builder_slot_set(builder,
+ field->offset_slot,
+ *pos - tuple);
+ }
+ if (validate)
bit_clear(required_fields, field->id);
}
/*
@@ -927,14 +873,76 @@ tuple_field_map_create(struct tuple_format *format, const char *tuple,
if ((type == MP_ARRAY || type == MP_MAP) &&
!mp_stack_is_full(&stack) && field != NULL) {
uint32_t size = type == MP_ARRAY ?
- mp_decode_array(&pos) :
- mp_decode_map(&pos);
+ mp_decode_array(pos) :
+ mp_decode_map(pos);
mp_stack_push(&stack, type, size);
parent = &field->token;
} else {
- mp_next(&pos);
+ mp_next(pos);
}
}
+end:
+ return 0;
+}
+
+/** @sa declaration for details. */
+int
+tuple_field_map_create(struct field_map_builder *builder,
+ struct tuple_format *format, const char *tuple,
+ bool validate)
+{
+ struct region *region = &fiber()->gc;
+ if (tuple_format_field_count(format) == 0) {
+ /* Nothing to initialize */
+ if (field_map_builder_create(builder, 0, region) != 0)
+ unreachable();
+ return 0;
+ }
+ if (field_map_builder_create(builder, format->field_map_size,
+ region) != 0)
+ return -1;
+ const char *pos = tuple;
+ /* Check to see if the tuple has a sufficient number of fields. */
+ uint32_t field_count = mp_decode_array(&pos);
+ if (validate && format->exact_field_count > 0 &&
+ format->exact_field_count != field_count) {
+ diag_set(ClientError, ER_EXACT_FIELD_COUNT,
+ (unsigned) field_count,
+ (unsigned) format->exact_field_count);
+ return -1;
+ }
+ /*
+ * Allocate a field bitmap that will be used for checking
+ * that all mandatory fields are present.
+ */
+ void *required_fields = NULL;
+ size_t required_fields_sz = 0;
+ if (validate) {
+ required_fields_sz = bitmap_size(format->total_field_count);
+ required_fields = region_alloc(region, required_fields_sz);
+ if (required_fields == NULL) {
+ diag_set(OutOfMemory, required_fields_sz,
+ "region", "required field bitmap");
+ return -1;
+ }
+ memcpy(required_fields, format->required_fields,
+ required_fields_sz);
+ }
+ /*
+ * Initialize the tuple field map and validate field types.
+ */
+ if (field_count == 0) {
+ /* Empty tuple, nothing to do. */
+ goto finish;
+ }
+ uint32_t defined_field_count = MIN(field_count, validate ?
+ tuple_format_field_count(format) :
+ format->index_field_count);
+ assert(!validate || required_fields != NULL);
+ if (tuple_field_map_initialize(builder, format, &pos, tuple,
+ defined_field_count, required_fields,
+ region) != 0)
+ return -1;
finish:
/*
* Check the required field bitmap for missing fields.
@@ -946,19 +954,15 @@ finish:
size_t id = bit_iterator_next(&it);
if (id < SIZE_MAX) {
/* A field is missing, report an error. */
- field = tuple_format_field_by_id(format, id);
+ struct tuple_field *field =
+ tuple_format_field_by_id(format, id);
assert(field != NULL);
diag_set(ClientError, ER_FIELD_MISSING,
tuple_field_path(field));
- goto error;
+ return -1;
}
}
-out:
- *field_map = (uint32_t *)((char *)*field_map - *field_map_size);
- return rc;
-error:
- rc = -1;
- goto out;
+ return 0;
}
uint32_t
diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h
index 22a0fb232..6874374cc 100644
--- a/src/box/tuple_format.h
+++ b/src/box/tuple_format.h
@@ -36,6 +36,7 @@
#include "errinj.h"
#include "json/json.h"
#include "tuple_dictionary.h"
+#include "tuple_field_map.h"
#if defined(__cplusplus)
extern "C" {
@@ -401,9 +402,9 @@ box_tuple_format_unref(box_tuple_format_t *format);
* tuple + off_i = indexed_field_i;
*/
int
-tuple_field_map_create(struct tuple_format *format, const char *tuple,
- bool validate, uint32_t **field_map,
- uint32_t *field_map_size);
+tuple_field_map_create(struct field_map_builder *builder,
+ struct tuple_format *format, const char *tuple,
+ bool validate);
/**
* Initialize tuple format subsystem.
diff --git a/src/box/vy_stmt.c b/src/box/vy_stmt.c
index 5d1e10f07..e355e3650 100644
--- a/src/box/vy_stmt.c
+++ b/src/box/vy_stmt.c
@@ -314,10 +314,11 @@ vy_stmt_new_with_ops(struct tuple_format *format, const char *tuple_begin,
* tuples inserted into a space are validated explicitly
* with tuple_validate() anyway.
*/
- uint32_t *field_map, field_map_size;
- if (tuple_field_map_create(format, tuple_begin, false, &field_map,
- &field_map_size) != 0)
+ struct field_map_builder builder;
+ if (tuple_field_map_create(&builder, format, tuple_begin, false) != 0)
goto end;
+ uint32_t field_map_size = field_map_builder_size(&builder);
+ assert(field_map_size == format->field_map_size);
/*
* Allocate stmt. Offsets: one per key part + offset of the
* statement end.
@@ -330,7 +331,7 @@ vy_stmt_new_with_ops(struct tuple_format *format, const char *tuple_begin,
/* Copy MsgPack data */
char *raw = (char *) tuple_data(stmt);
char *wpos = raw;
- memcpy(wpos - field_map_size, field_map, field_map_size);
+ field_map_builder_build(&builder, wpos - field_map_size);
memcpy(wpos, tuple_begin, mpsize);
wpos += mpsize;
for (struct iovec *op = ops, *end = ops + op_count;
--
2.21.0
More information about the Tarantool-patches
mailing list