// Byte swap header from network byte order (big endian) to current machine byte order. void as_msg_swap_header_from_be(as_msg *m) { m->generation = cf_swap_from_be32(m->generation); m->record_ttl = cf_swap_from_be32(m->record_ttl); m->transaction_ttl = cf_swap_from_be32(m->transaction_ttl); m->n_fields = cf_swap_from_be16(m->n_fields); m->n_ops= cf_swap_from_be16(m->n_ops); }
static void as_uv_connected(uv_connect_t* req, int status) { if (uv_is_closing((uv_handle_t*)req->handle)) { return; } as_event_command* cmd = req->data; if (status == 0) { if (cmd->cluster->user) { as_uv_auth_write_start(cmd, req->handle); } else { as_uv_command_write_start(cmd, req->handle); } } else if (status != UV_ECANCELED) { as_node* node = cmd->node; as_address* primary = as_vector_get(&node->addresses, node->address_index); as_error err; as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to connect: %s %s:%d", node->name, primary->name, (int)cf_swap_from_be16(primary->addr.sin_port)); as_uv_connect_error(cmd, &err); } }
bool cdt_process_state_init(cdt_process_state *cdt_state, const as_msg_op *op) { const uint8_t *data = op->name + op->name_sz; uint32_t size = op->op_sz - 4 - op->name_sz; if (size < sizeof(uint16_t)) { cf_warning(AS_PARTICLE, "cdt_parse_state_init() as_msg_op data too small to be valid: size=%u", size); return false; } const uint16_t *type_ptr = (const uint16_t *)data; cdt_state->type = cf_swap_from_be16(*type_ptr); cdt_state->pk.buffer = data + sizeof(uint16_t); cdt_state->pk.length = size - sizeof(uint16_t); cdt_state->pk.offset = 0; int ele_count = (cdt_state->pk.length == 0) ? 0 : as_unpack_list_header_element_count(&cdt_state->pk); if (ele_count < 0) { cf_warning(AS_PARTICLE, "cdt_parse_state_init() unpack list header failed: size=%u type=%u", size, cdt_state->type); return false; } cdt_state->ele_count = ele_count; return true; }
// TODO - old pickle - remove in "six months". int old_record_apply_ssd_single_bin(as_remote_record *rr, as_storage_rd *rd, bool *is_delete) { as_namespace* ns = rr->rsv->ns; as_record* r = rd->r; uint16_t n_new_bins = cf_swap_from_be16(*(uint16_t *)rr->pickle); if (n_new_bins > 1) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: single-bin got %u bins ", ns->name, n_new_bins); return AS_ERR_UNKNOWN; } as_bin stack_bin = { { 0 } }; rd->n_bins = 1; rd->bins = &stack_bin; // Fill the new bin and particle. cf_ll_buf_define(particles_llb, STACK_PARTICLES_SIZE); int result; if (n_new_bins == 1 && (result = unpickle_bins(rr, rd, &particles_llb)) != 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed unpickle bin ", ns->name); cf_ll_buf_free(&particles_llb); return result; } // Apply changes to metadata in as_index needed for and writing. index_metadata old_metadata; update_index_metadata(rr, &old_metadata, r); // Prepare to store or drop key, as determined by message. rd->key = rr->key; rd->key_size = rr->key_size; // Write the record to storage. if ((result = as_record_write_from_pickle(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed write ", ns->name); unwind_index_metadata(&old_metadata, r); cf_ll_buf_free(&particles_llb); return -result; } // Now ok to store or drop key, as determined by message. as_record_finalize_key(r, ns, rd->key, rd->key_size); *is_delete = n_new_bins == 0; cf_ll_buf_free(&particles_llb); return AS_OK; }
static int as_node_create_connection(as_node* node, int* fd) { // Create a non-blocking socket. *fd = cf_socket_create_nb(); if (*fd == -1) { // Local problem - socket create failed. cf_debug("Socket create failed for %s", node->name); return CITRUSLEAF_FAIL_CLIENT; } // Try primary address. as_address* primary = as_vector_get(&node->addresses, node->address_index); if (cf_socket_start_connect_nb(*fd, &primary->addr) == 0) { // Connection started ok - we have our socket. return as_node_authenticate_connection(node, fd); } // Try other addresses. as_vector* addresses = &node->addresses; for (uint32_t i = 0; i < addresses->size; i++) { as_address* address = as_vector_get(addresses, i); // Address points into alias array, so pointer comparison is sufficient. if (address != primary) { if (cf_socket_start_connect_nb(*fd, &address->addr) == 0) { // Replace invalid primary address with valid alias. // Other threads may not see this change immediately. // It's just a hint, not a requirement to try this new address first. cf_debug("Change node address %s %s:%d", node->name, address->name, (int)cf_swap_from_be16(address->addr.sin_port)); ck_pr_store_32(&node->address_index, i); return as_node_authenticate_connection(node, fd); } } } // Couldn't start a connection on any socket address - close the socket. cf_info("Failed to connect: %s %s:%d", node->name, primary->name, (int)cf_swap_from_be16(primary->addr.sin_port)); cf_close(*fd); *fd = -1; return CITRUSLEAF_FAIL_UNAVAILABLE; }
static void as_cluster_find_nodes_to_add(as_cluster* cluster, as_vector* /* <as_host> */ friends, as_vector* /* <as_node*> */ nodes_to_add) { as_error err; as_error_init(&err); as_vector addresses; as_vector_inita(&addresses, sizeof(struct sockaddr_in), 5); as_node_info node_info; for (uint32_t i = 0; i < friends->size; i++) { as_host* friend = as_vector_get(friends, i); as_vector_clear(&addresses); as_status status = as_lookup(&err, friend->name, friend->port, &addresses); if (status != AEROSPIKE_OK) { as_log_warn("%s %s", as_error_string(status), err.message); continue; } for (uint32_t i = 0; i < addresses.size; i++) { struct sockaddr_in* addr = as_vector_get(&addresses, i); status = as_lookup_node(cluster, &err, addr, &node_info); if (status == AEROSPIKE_OK) { as_node* node = as_cluster_find_node(cluster->nodes, nodes_to_add, node_info.name); if (node) { // Duplicate node name found. This usually occurs when the server // services list contains both internal and external IP addresses // for the same node. Add new host to list of alias filters // and do not add new node. as_close(node_info.fd); as_address* a = as_node_get_address_full(node); as_log_info("Node %s:%d already exists with nodeid %s and address %s:%d", friend->name, friend->port, node->name, a->name, (int)cf_swap_from_be16(a->addr.sin_port)); node->friends++; as_node_add_address(node, friend, addr); continue; } node = as_node_create(cluster, friend, addr, &node_info); as_address* a = as_node_get_address_full(node); as_log_info("Add node %s %s:%d", node_info.name, a->name, (int)cf_swap_from_be16(a->addr.sin_port)); as_vector_append(nodes_to_add, &node); } else { as_log_warn("Failed to connect to friend %s:%d. %s %s", friend->name, friend->port, as_error_string(status), err.message); } }
int32_t geojson_size_from_wire(const uint8_t *wire_value, uint32_t value_size) { // NOTE - Unfortunately we would need to run the JSON parser and region // coverer to find out exactly how many cells we need to allocate for this // particle. // // For now we always allocate the maximum number of cells (MAX_REGION_CELLS) // for the in-memory particle. // // For now also ignore any incoming cells entirely. uint8_t const *incp = (uint8_t const *)wire_value + 1; uint16_t incells = cf_swap_from_be16(*(uint16_t const *)incp); size_t incellsz = incells * sizeof(uint64_t); size_t injsonsz = value_size - sizeof(uint8_t) - sizeof(uint16_t) - incellsz; return (int32_t)(sizeof(geojson_mem) + (MAX_REGION_CELLS * sizeof(uint64_t)) + injsonsz); }
static as_status as_cluster_seed_nodes(as_cluster* cluster, as_error* err, bool enable_warnings) { // Add all nodes at once to avoid copying entire array multiple times. as_vector nodes_to_add; as_vector_inita(&nodes_to_add, sizeof(as_node*), 64); as_vector addresses; as_vector_inita(&addresses, sizeof(struct sockaddr_in), 5); as_node_info node_info; as_error error_local; as_error_init(&error_local); // AEROSPIKE_ERR_TIMEOUT doesn't come with a message; make sure it's initialized. as_status status = AEROSPIKE_OK; as_seeds* seeds = as_seeds_reserve(cluster); for (uint32_t i = 0; i < seeds->size; i++) { as_seed* seed = &seeds->array[i]; as_vector_clear(&addresses); status = as_lookup(&error_local, seed->name, seed->port, &addresses); if (status != AEROSPIKE_OK) { if (enable_warnings) { as_log_warn("Failed to lookup %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message); } continue; } for (uint32_t i = 0; i < addresses.size; i++) { struct sockaddr_in* addr = as_vector_get(&addresses, i); status = as_lookup_node(cluster, &error_local, addr, &node_info); if (status == AEROSPIKE_OK) { as_host host; if (as_strncpy(host.name, seed->name, sizeof(host.name))) { as_log_warn("Hostname has been truncated: %s", host.name); } host.port = seed->port; as_node* node = as_cluster_find_node_in_vector(&nodes_to_add, node_info.name); if (node) { as_close(node_info.fd); as_node_add_address(node, &host, addr); } else { node = as_node_create(cluster, &host, addr, &node_info); as_address* a = as_node_get_address_full(node); as_log_info("Add node %s %s:%d", node->name, a->name, (int)cf_swap_from_be16(a->addr.sin_port)); as_vector_append(&nodes_to_add, &node); } } else { if (enable_warnings) { as_log_warn("Failed to connect to seed %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message); } } } } as_seeds_release(seeds); if (nodes_to_add.size > 0) { as_cluster_add_nodes(cluster, &nodes_to_add); status = AEROSPIKE_OK; } else { status = as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to seed cluster"); } as_vector_destroy(&nodes_to_add); as_vector_destroy(&addresses); return status; }
static void as_ev_connect(as_event_command* cmd) { int fd = as_event_create_socket(cmd); if (fd < 0) { return; } // Try primary address. as_node* node = cmd->node; as_address* primary = as_vector_get(&node->addresses, node->address_index); // Attempt non-blocking connection. if (connect(fd, (struct sockaddr*)&primary->addr, sizeof(struct sockaddr)) == 0) { as_ev_watcher_init(cmd, fd); return; } // Check if connection is in progress. if (errno == EINPROGRESS) { as_ev_watcher_init(cmd, fd); return; } // Try other addresses. as_vector* addresses = &node->addresses; for (uint32_t i = 0; i < addresses->size; i++) { as_address* address = as_vector_get(addresses, i); // Address points into alias array, so pointer comparison is sufficient. if (address != primary) { if (connect(fd, (struct sockaddr*)&address->addr, sizeof(struct sockaddr)) == 0) { // Replace invalid primary address with valid alias. // Other threads may not see this change immediately. // It's just a hint, not a requirement to try this new address first. as_log_debug("Change node address %s %s:%d", node->name, address->name, (int)cf_swap_from_be16(address->addr.sin_port)); ck_pr_store_32(&node->address_index, i); as_ev_watcher_init(cmd, fd); return; } // Check if connection is in progress. if (errno == EINPROGRESS) { // Replace invalid primary address with valid alias. // Other threads may not see this change immediately. // It's just a hint, not a requirement to try this new address first. as_log_debug("Change node address %s %s:%d", node->name, address->name, (int)cf_swap_from_be16(address->addr.sin_port)); ck_pr_store_32(&node->address_index, i); // Connection hasn't finished. as_ev_watcher_init(cmd, fd); return; } } } // Failed to start a connection on any socket address. as_error err; as_error_update(&err, AEROSPIKE_ERR_ASYNC_CONNECTION, "Failed to connect: %s %s:%d", node->name, primary->name, (int)cf_swap_from_be16(primary->addr.sin_port)); as_event_connect_error(cmd, &err, fd); }
// TODO - old pickle - remove in "six months". int old_record_apply_dim(as_remote_record *rr, as_storage_rd *rd, bool skip_sindex, bool *is_delete) { as_namespace* ns = rr->rsv->ns; as_record* r = rd->r; // Set rd->n_bins! as_storage_rd_load_n_bins(rd); // Set rd->bins! as_storage_rd_load_bins(rd, NULL); // For memory accounting, note current usage. uint64_t memory_bytes = as_storage_record_get_n_bytes_memory(rd); // Keep old bins intact for sindex adjustment and unwinding. uint16_t n_old_bins = rd->n_bins; as_bin* old_bins = rd->bins; uint16_t n_new_bins = cf_swap_from_be16(*(uint16_t *)rr->pickle); as_bin new_bins[n_new_bins]; memset(new_bins, 0, sizeof(new_bins)); rd->n_bins = n_new_bins; rd->bins = new_bins; // Fill the new bins and particles. int result = unpickle_bins(rr, rd, NULL); if (result != 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed unpickle bins ", ns->name); destroy_stack_bins(new_bins, n_new_bins); return result; } // Apply changes to metadata in as_index needed for and writing. index_metadata old_metadata; update_index_metadata(rr, &old_metadata, r); // Prepare to store or drop key, as determined by message. rd->key = rr->key; rd->key_size = rr->key_size; // Write the record to storage. if ((result = as_record_write_from_pickle(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed write ", ns->name); unwind_index_metadata(&old_metadata, r); destroy_stack_bins(new_bins, n_new_bins); return -result; } // Success - adjust sindex, looking at old and new bins. if (! (skip_sindex && next_generation(r->generation, (uint16_t)rr->generation, ns)) && record_has_sindex(r, ns)) { write_sindex_update(ns, as_index_get_set_name(r, ns), rr->keyd, old_bins, n_old_bins, new_bins, n_new_bins); } // Cleanup - destroy relevant bins, can't unwind after. destroy_stack_bins(old_bins, n_old_bins); // Fill out new_bin_space. as_bin_space* new_bin_space = NULL; if (n_new_bins != 0) { new_bin_space = (as_bin_space*) cf_malloc_ns(sizeof(as_bin_space) + sizeof(new_bins)); new_bin_space->n_bins = rd->n_bins; memcpy((void*)new_bin_space->bins, new_bins, sizeof(new_bins)); } // Swizzle the index element's as_bin_space pointer. as_bin_space* old_bin_space = as_index_get_bin_space(r); if (old_bin_space) { cf_free(old_bin_space); } as_index_set_bin_space(r, new_bin_space); // Now ok to store or drop key, as determined by message. as_record_finalize_key(r, ns, rd->key, rd->key_size); as_storage_record_adjust_mem_stats(rd, memory_bytes); *is_delete = n_new_bins == 0; return AS_OK; }
// TODO - old pickle - remove in "six months". int old_record_apply_dim_single_bin(as_remote_record *rr, as_storage_rd *rd, bool *is_delete) { as_namespace* ns = rr->rsv->ns; as_record* r = rd->r; rd->n_bins = 1; // Set rd->bins! as_storage_rd_load_bins(rd, NULL); // For memory accounting, note current usage. uint64_t memory_bytes = 0; // TODO - as_storage_record_get_n_bytes_memory() could check bins in use. if (as_bin_inuse(rd->bins)) { memory_bytes = as_storage_record_get_n_bytes_memory(rd); } uint16_t n_new_bins = cf_swap_from_be16(*(uint16_t *)rr->pickle); if (n_new_bins > 1) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: single-bin got %u bins ", ns->name, n_new_bins); return AS_ERR_UNKNOWN; } // Keep old bin intact for unwinding, clear record bin for incoming. as_bin old_bin; as_single_bin_copy(&old_bin, rd->bins); as_bin_set_empty(rd->bins); int result; // Fill the new bins and particles. if (n_new_bins == 1 && (result = unpickle_bins(rr, rd, NULL)) != 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed unpickle bin ", ns->name); unwind_dim_single_bin(&old_bin, rd->bins); return result; } // Apply changes to metadata in as_index needed for and writing. index_metadata old_metadata; update_index_metadata(rr, &old_metadata, r); // Write the record to storage. if ((result = as_record_write_from_pickle(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed write ", ns->name); unwind_index_metadata(&old_metadata, r); unwind_dim_single_bin(&old_bin, rd->bins); return -result; } // Cleanup - destroy old bin, can't unwind after. as_bin_particle_destroy(&old_bin, true); as_storage_record_adjust_mem_stats(rd, memory_bytes); *is_delete = n_new_bins == 0; return AS_OK; }
// TODO - old pickle - remove in "six months". int old_record_apply_ssd(as_remote_record *rr, as_storage_rd *rd, bool skip_sindex, bool *is_delete) { as_namespace* ns = rr->rsv->ns; as_record* r = rd->r; bool has_sindex = ! (skip_sindex && next_generation(r->generation, (uint16_t)rr->generation, ns)) && record_has_sindex(r, ns); uint16_t n_old_bins = 0; int result; if (has_sindex) { // Set rd->n_bins! if ((result = as_storage_rd_load_n_bins(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed load n-bins ", ns->name); return -result; } n_old_bins = rd->n_bins; } as_bin old_bins[n_old_bins]; if (has_sindex) { // Set rd->bins! if ((result = as_storage_rd_load_bins(rd, old_bins)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed load bins ", ns->name); return -result; } } // Stack space for resulting record's bins. uint16_t n_new_bins = cf_swap_from_be16(*(uint16_t *)rr->pickle); as_bin new_bins[n_new_bins]; memset(new_bins, 0, sizeof(new_bins)); rd->n_bins = n_new_bins; rd->bins = new_bins; // Fill the new bins and particles. cf_ll_buf_define(particles_llb, STACK_PARTICLES_SIZE); if ((result = unpickle_bins(rr, rd, &particles_llb)) != 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed unpickle bins ", ns->name); cf_ll_buf_free(&particles_llb); return result; } // Apply changes to metadata in as_index needed for and writing. index_metadata old_metadata; update_index_metadata(rr, &old_metadata, r); // Prepare to store or drop key, as determined by message. rd->key = rr->key; rd->key_size = rr->key_size; // Write the record to storage. if ((result = as_record_write_from_pickle(rd)) < 0) { cf_warning_digest(AS_RECORD, rr->keyd, "{%s} record replace: failed write ", ns->name); unwind_index_metadata(&old_metadata, r); cf_ll_buf_free(&particles_llb); return -result; } // Success - adjust sindex, looking at old and new bins. if (has_sindex) { write_sindex_update(ns, as_index_get_set_name(r, ns), rr->keyd, old_bins, n_old_bins, new_bins, n_new_bins); } // Now ok to store or drop key, as determined by message. as_record_finalize_key(r, ns, rd->key, rd->key_size); *is_delete = n_new_bins == 0; cf_ll_buf_free(&particles_llb); return AS_OK; }
uint8_t* as_command_parse_bins(as_record* rec, uint8_t* p, uint32_t n_bins, bool deserialize) { as_bin* bin = rec->bins.entries; // Parse bins for (uint32_t i = 0; i < n_bins; i++, bin++) { uint32_t op_size = cf_swap_from_be32(*(uint32_t*)p); p += 5; uint8_t type = *p; p += 2; uint8_t name_size = *p++; uint8_t name_len = (name_size <= AS_BIN_NAME_MAX_LEN)? name_size : AS_BIN_NAME_MAX_LEN; memcpy(bin->name, p, name_len); bin->name[name_len] = 0; p += name_size; uint32_t value_size = (op_size - (name_size + 4)); switch (type) { case AS_BYTES_UNDEF: { bin->valuep = (as_bin_value*)&as_nil; break; } case AS_BYTES_INTEGER: { int64_t value; if (as_command_bytes_to_int(p, value_size, &value) == 0) { as_integer_init((as_integer*)&bin->value, value); bin->valuep = &bin->value; } break; } case AS_BYTES_DOUBLE: { double value = cf_swap_from_big_float64(*(double*)p); as_double_init((as_double*)&bin->value, value); bin->valuep = &bin->value; break; } case AS_BYTES_STRING: { char* value = cf_malloc(value_size + 1); memcpy(value, p, value_size); value[value_size] = 0; as_string_init_wlen((as_string*)&bin->value, (char*)value, value_size, true); bin->valuep = &bin->value; break; } case AS_BYTES_GEOJSON: { uint8_t * ptr = p; // skip flags ptr++; // ncells uint16_t ncells = cf_swap_from_be16(* (uint16_t *) ptr); ptr += sizeof(uint16_t); // skip any cells ptr += sizeof(uint64_t) * ncells; // Use the json bytes. size_t jsonsz = value_size - 1 - 2 - (ncells * sizeof(uint64_t)); char* v = cf_malloc(jsonsz + 1); memcpy(v, ptr, jsonsz); v[jsonsz] = 0; as_geojson_init_wlen((as_geojson*)&bin->value, (char*)v, jsonsz, true); bin->valuep = &bin->value; break; } case AS_BYTES_LDT: case AS_BYTES_LIST: case AS_BYTES_MAP: { if (deserialize) { as_val* value = 0; as_buffer buffer; buffer.data = p; buffer.size = value_size; as_serializer ser; as_msgpack_init(&ser); as_serializer_deserialize(&ser, &buffer, &value); as_serializer_destroy(&ser); bin->valuep = (as_bin_value*)value; } else { void* value = cf_malloc(value_size); memcpy(value, p, value_size); as_bytes_init_wrap((as_bytes*)&bin->value, value, value_size, true); bin->value.bytes.type = (as_bytes_type)type; bin->valuep = &bin->value; } break; } default: { void* value = cf_malloc(value_size); memcpy(value, p, value_size); as_bytes_init_wrap((as_bytes*)&bin->value, value, value_size, true); bin->value.bytes.type = (as_bytes_type)type; bin->valuep = &bin->value; break; } } rec->bins.size++; p += value_size; } return p; }
static void as_command_parse_value(uint8_t* p, uint8_t type, uint32_t value_size, as_val** value) { // Allocate values on heap. switch (type) { case AS_BYTES_UNDEF: { *value = (as_val*)&as_nil; break; } case AS_BYTES_INTEGER: { int64_t v = 0; as_command_bytes_to_int(p, value_size, &v); *value = (as_val*)as_integer_new(v); break; } case AS_BYTES_DOUBLE: { double v = cf_swap_from_big_float64(*(double*)p); *value = (as_val*)as_double_new(v); break; } case AS_BYTES_STRING: { char* v = cf_malloc(value_size + 1); memcpy(v, p, value_size); v[value_size] = 0; *value = (as_val*)as_string_new_wlen(v, value_size, true); break; } case AS_BYTES_GEOJSON: { uint8_t * ptr = p; // skip flags ptr++; // ncells uint16_t ncells = cf_swap_from_be16(* (uint16_t *) ptr); ptr += sizeof(uint16_t); // skip any cells ptr += sizeof(uint64_t) * ncells; // Use the json bytes. size_t jsonsz = value_size - 1 - 2 - (ncells * sizeof(uint64_t)); char* v = cf_malloc(jsonsz + 1); memcpy(v, ptr, jsonsz); v[jsonsz] = 0; *value = (as_val*) as_geojson_new_wlen(v, jsonsz, true); break; } case AS_BYTES_LIST: case AS_BYTES_MAP: { as_buffer buffer; buffer.data = p; buffer.size = value_size; as_serializer ser; as_msgpack_init(&ser); as_serializer_deserialize(&ser, &buffer, value); as_serializer_destroy(&ser); break; } default: { void* v = cf_malloc(value_size); memcpy(v, p, value_size); *value = (as_val*)as_bytes_new_wrap(v, value_size, true); break; } } }
int geojson_from_wire(as_particle_type wire_type, const uint8_t *wire_value, uint32_t value_size, as_particle **pp) { uint8_t const *incp = (uint8_t const *)wire_value + 1; uint16_t incells = cf_swap_from_be16(*(uint16_t const *)incp); size_t incellsz = incells * sizeof(uint64_t); char const *injsonptr = (char const *)incp + sizeof(uint16_t) + incellsz; size_t injsonsz = value_size - sizeof(uint8_t) - sizeof(uint16_t) - incellsz; // We ignore any incoming cells entirely. uint64_t cellid = 0; geo_region_t region = NULL; if (! geo_parse(NULL, injsonptr, injsonsz, &cellid, ®ion)) { cf_warning(AS_PARTICLE, "geo_parse failed"); return -AS_PROTO_RESULT_FAIL_GEO_INVALID_GEOJSON; } if (cellid && region) { geo_region_destroy(region); cf_warning(AS_PARTICLE, "geo_parse found both point and region"); return -AS_PROTO_RESULT_FAIL_GEO_INVALID_GEOJSON; } if (! cellid && ! region) { cf_warning(AS_PARTICLE, "geo_parse found neither point nor region"); return -AS_PROTO_RESULT_FAIL_GEO_INVALID_GEOJSON; } geojson_mem *p_geojson_mem = (geojson_mem *)*pp; p_geojson_mem->type = wire_type; // We'll come back and set the size at the end. uint64_t *p_outcells = (uint64_t *)p_geojson_mem->data; p_geojson_mem->flags = 0; if (cellid) { // POINT p_geojson_mem->flags &= ~GEOJSON_ISREGION; p_geojson_mem->ncells = 1; p_outcells[0] = cellid; } else { // REGION p_geojson_mem->flags |= GEOJSON_ISREGION; int numcells; if (! geo_region_cover(NULL, region, MAX_REGION_CELLS, p_outcells, NULL, NULL, &numcells)) { geo_region_destroy(region); cf_warning(AS_PARTICLE, "geo_region_cover failed"); return -AS_PROTO_RESULT_FAIL_GEO_INVALID_GEOJSON; } p_geojson_mem->ncells = numcells; } if (region) { geo_region_destroy(region); } // Copy the JSON into place. char *p_outjson = (char *)&p_outcells[p_geojson_mem->ncells]; memcpy(p_outjson, injsonptr, injsonsz); // Set the actual size; we will waste some space at the end of the allocated // particle. p_geojson_mem->sz = sizeof(uint8_t) + sizeof(uint16_t) + (p_geojson_mem->ncells * sizeof(uint64_t)) + injsonsz; return AS_PROTO_RESULT_OK; }