Mercurial > hg > nginx-ranges
diff src/http/modules/ngx_http_geo_module.c @ 635:e67b227c8dbb default tip
Merge with current.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 25 Apr 2011 04:07:55 +0400 |
parents | b4dcae568a2a |
children |
line wrap: on
line diff
--- a/src/http/modules/ngx_http_geo_module.c +++ b/src/http/modules/ngx_http_geo_module.c @@ -10,41 +10,53 @@ typedef struct { + ngx_http_variable_value_t *value; u_short start; u_short end; - ngx_http_variable_value_t *value; } ngx_http_geo_range_t; typedef struct { - ngx_http_geo_range_t *ranges; - ngx_uint_t n; -} ngx_http_geo_low_ranges_t; - - -typedef struct { - ngx_http_geo_low_ranges_t low[0x10000]; + ngx_http_geo_range_t **low; ngx_http_variable_value_t *default_value; } ngx_http_geo_high_ranges_t; typedef struct { + ngx_str_node_t sn; + ngx_http_variable_value_t *value; + size_t offset; +} ngx_http_geo_variable_value_node_t; + + +typedef struct { ngx_http_variable_value_t *value; ngx_str_t *net; - ngx_http_geo_high_ranges_t *high; + ngx_http_geo_high_ranges_t high; ngx_radix_tree_t *tree; ngx_rbtree_t rbtree; ngx_rbtree_node_t sentinel; ngx_array_t *proxies; ngx_pool_t *pool; ngx_pool_t *temp_pool; + + size_t data_size; + + ngx_str_t include_name; + ngx_uint_t includes; + ngx_uint_t entries; + + unsigned ranges:1; + unsigned outside_entries:1; + unsigned allow_binary_include:1; + unsigned binary_include:1; } ngx_http_geo_conf_ctx_t; typedef struct { union { ngx_radix_tree_t *tree; - ngx_http_geo_high_ranges_t *high; + ngx_http_geo_high_ranges_t high; } u; ngx_array_t *proxies; @@ -73,6 +85,13 @@ static char *ngx_http_geo_add_proxy(ngx_ ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr); static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr); +static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *name); +static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf, + ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name); +static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx); +static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); static ngx_command_t ngx_http_geo_commands[] = { @@ -119,6 +138,20 @@ ngx_module_t ngx_http_geo_module = { }; +typedef struct { + u_char GEORNG[6]; + u_char version; + u_char ptr_size; + uint32_t endianess; + uint32_t crc32; +} ngx_http_geo_header_t; + + +static ngx_http_geo_header_t ngx_http_geo_header = { + { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0 +}; + + /* AF_INET only */ static ngx_int_t @@ -148,23 +181,24 @@ ngx_http_geo_range_variable(ngx_http_req ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; in_addr_t addr; - ngx_uint_t i, n; + ngx_uint_t n; ngx_http_geo_range_t *range; - *v = *ctx->u.high->default_value; + *v = *ctx->u.high.default_value; addr = ngx_http_geo_addr(r, ctx); - range = ctx->u.high->low[addr >> 16].ranges; - - n = addr & 0xffff; + range = ctx->u.high.low[addr >> 16]; - for (i = 0; i < ctx->u.high->low[addr >> 16].n; i++) { - if (n >= (ngx_uint_t) range[i].start - && n <= (ngx_uint_t) range[i].end) - { - *v = *range[i].value; - } + if (range) { + n = addr & 0xffff; + do { + if (n >= (ngx_uint_t) range->start && n <= (ngx_uint_t) range->end) + { + *v = *range->value; + break; + } + } while ((++range)->value); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -256,6 +290,7 @@ static char * ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; + void **p; size_t len; ngx_str_t *value, name; ngx_uint_t i; @@ -302,18 +337,20 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c return NGX_CONF_ERROR; } + ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t)); + ctx.temp_pool = ngx_create_pool(16384, cf->log); if (ctx.temp_pool == NULL) { return NGX_CONF_ERROR; } - ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, - ngx_http_variable_value_rbtree_insert); + ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value); - ctx.high = NULL; - ctx.tree = NULL; - ctx.proxies = NULL; ctx.pool = cf->pool; + ctx.data_size = sizeof(ngx_http_geo_header_t) + + sizeof(ngx_http_variable_value_t) + + 0x10000 * sizeof(ngx_http_geo_range_t *); + ctx.allow_binary_include = 1; save = *cf; cf->pool = pool; @@ -327,25 +364,35 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c geo->proxies = ctx.proxies; - if (ctx.high) { + if (ctx.high.low) { + + if (!ctx.binary_include) { + for (i = 0; i < 0x10000; i++) { + a = (ngx_array_t *) ctx.high.low[i]; + + if (a == NULL || a->nelts == 0) { + continue; + } - for (i = 0; i < 0x10000; i++) { - a = (ngx_array_t *) ctx.high->low[i].ranges; + len = a->nelts * sizeof(ngx_http_geo_range_t); - if (a == NULL || a->nelts == 0) { - continue; + ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *)); + if (ctx.high.low[i] == NULL) { + return NGX_CONF_ERROR; + } + + p = (void **) ngx_cpymem(ctx.high.low[i], a->elts, len); + *p = NULL; + ctx.data_size += len + sizeof(void *); } - ctx.high->low[i].n = a->nelts; - - len = a->nelts * sizeof(ngx_http_geo_range_t); - - ctx.high->low[i].ranges = ngx_palloc(cf->pool, len); - if (ctx.high->low[i].ranges == NULL ){ - return NGX_CONF_ERROR; + if (ctx.allow_binary_include + && !ctx.outside_entries + && ctx.entries > 100000 + && ctx.includes == 1) + { + ngx_http_geo_create_binary_base(&ctx); } - - ngx_memcpy(ctx.high->low[i].ranges, a->elts, len); } geo->u.high = ctx.high; @@ -353,13 +400,13 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c var->get_handler = ngx_http_geo_range_variable; var->data = (uintptr_t) geo; + if (ctx.high.default_value == NULL) { + ctx.high.default_value = &ngx_http_variable_null_value; + } + ngx_destroy_pool(ctx.temp_pool); ngx_destroy_pool(pool); - if (ctx.high->default_value == NULL) { - ctx.high->default_value = &ngx_http_variable_null_value; - } - } else { if (ctx.tree == NULL) { ctx.tree = ngx_radix_tree_create(cf->pool, -1); @@ -396,7 +443,7 @@ static char * ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { char *rv; - ngx_str_t *value, file; + ngx_str_t *value; ngx_cidr_t cidr; ngx_http_geo_conf_ctx_t *ctx; @@ -415,11 +462,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command goto failed; } - ctx->high = ngx_pcalloc(ctx->pool, - sizeof(ngx_http_geo_high_ranges_t)); - if (ctx->high == NULL) { - goto failed; - } + ctx->ranges = 1; rv = NGX_CONF_OK; @@ -435,20 +478,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command if (ngx_strcmp(value[0].data, "include") == 0) { - file.len = value[1].len++; - - file.data = ngx_pstrdup(ctx->temp_pool, &value[1]); - if (file.data == NULL) { - goto failed; - } - - if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){ - goto failed; - } - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); - - rv = ngx_conf_parse(cf, &file); + rv = ngx_http_geo_include(cf, ctx, &value[1]); goto done; @@ -463,7 +493,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command goto done; } - if (ctx->high) { + if (ctx->ranges) { rv = ngx_http_geo_range(cf, ctx, value); } else { @@ -488,30 +518,45 @@ static char * ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value) { - u_char *p, *last; - in_addr_t start, end; - ngx_str_t *net; - ngx_uint_t del; - ngx_http_variable_value_t *old; + u_char *p, *last; + in_addr_t start, end; + ngx_str_t *net; + ngx_uint_t del; if (ngx_strcmp(value[0].data, "default") == 0) { - old = ctx->high->default_value; - - ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]); - if (ctx->high->default_value == NULL) { - return NGX_CONF_ERROR; + if (ctx->high.default_value) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate default geo range value: \"%V\", old value: \"%v\"", + &value[1], ctx->high.default_value); } - if (old) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", - &value[0], ctx->high->default_value, old); + ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]); + if (ctx->high.default_value == NULL) { + return NGX_CONF_ERROR; } return NGX_CONF_OK; } + if (ctx->binary_include) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "binary geo range base \"%s\" may not be mixed with usual entries", + ctx->include_name.data); + return NGX_CONF_ERROR; + } + + if (ctx->high.low == NULL) { + ctx->high.low = ngx_pcalloc(ctx->pool, + 0x10000 * sizeof(ngx_http_geo_range_t *)); + if (ctx->high.low == NULL) { + return NGX_CONF_ERROR; + } + } + + ctx->entries++; + ctx->outside_entries = 1; + if (ngx_strcmp(value[0].data, "delete") == 0) { net = &value[1]; del = 1; @@ -606,7 +651,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, n e = 0xffff; } - a = (ngx_array_t *) ctx->high->low[h].ranges; + a = (ngx_array_t *) ctx->high.low[h]; if (a == NULL) { a = ngx_array_create(ctx->temp_pool, 64, @@ -615,7 +660,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, n return NGX_CONF_ERROR; } - ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a; + ctx->high.low[h] = (ngx_http_geo_range_t *) a; } i = a->nelts; @@ -640,7 +685,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, n range = a->elts; - ngx_memcpy(&range[i + 2], &range[i + 1], + ngx_memmove(&range[i + 2], &range[i + 1], (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) s; @@ -679,7 +724,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, n range = a->elts; - ngx_memcpy(&range[i + 3], &range[i + 1], + ngx_memmove(&range[i + 3], &range[i + 1], (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t)); range[i + 2].start = (u_short) (e + 1); @@ -707,7 +752,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, n range = a->elts; - ngx_memcpy(&range[i + 1], &range[i], + ngx_memmove(&range[i + 1], &range[i], (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) (e + 1); @@ -731,7 +776,7 @@ ngx_http_geo_add_range(ngx_conf_t *cf, n range = a->elts; - ngx_memcpy(&range[i + 2], &range[i + 1], + ngx_memmove(&range[i + 2], &range[i + 1], (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t)); range[i + 1].start = (u_short) s; @@ -803,7 +848,7 @@ ngx_http_geo_delete_range(ngx_conf_t *cf e = 0xffff; } - a = (ngx_array_t *) ctx->high->low[h].ranges; + a = (ngx_array_t *) ctx->high.low[h]; if (a == NULL) { warn = 1; @@ -816,7 +861,7 @@ ngx_http_geo_delete_range(ngx_conf_t *cf if (s == (ngx_uint_t) range[i].start && e == (ngx_uint_t) range[i].end) { - ngx_memcpy(&range[i], &range[i + 1], + ngx_memmove(&range[i], &range[i + 1], (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); a->nelts--; @@ -929,16 +974,17 @@ static ngx_http_variable_value_t * ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value) { - uint32_t hash; - ngx_http_variable_value_t *val; - ngx_http_variable_value_node_t *vvn; + uint32_t hash; + ngx_http_variable_value_t *val; + ngx_http_geo_variable_value_node_t *gvvn; hash = ngx_crc32_long(value->data, value->len); - val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash); + gvvn = (ngx_http_geo_variable_value_node_t *) + ngx_str_rbtree_lookup(&ctx->rbtree, value, hash); - if (val) { - return val; + if (gvvn) { + return gvvn->value; } val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); @@ -956,16 +1002,22 @@ ngx_http_geo_value(ngx_conf_t *cf, ngx_h val->no_cacheable = 0; val->not_found = 0; - vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t)); - if (vvn == NULL) { + gvvn = ngx_palloc(ctx->temp_pool, + sizeof(ngx_http_geo_variable_value_node_t)); + if (gvvn == NULL) { return NULL; } - vvn->node.key = hash; - vvn->len = val->len; - vvn->value = val; + gvvn->sn.node.key = hash; + gvvn->sn.str.len = val->len; + gvvn->sn.str.data = val->data; + gvvn->value = val; + gvvn->offset = 0; - ngx_rbtree_insert(&ctx->rbtree, &vvn->node); + ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node); + + ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len, + sizeof(void *)); return val; } @@ -1030,3 +1082,335 @@ ngx_http_geo_cidr_value(ngx_conf_t *cf, return NGX_OK; } + + +static char * +ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *name) +{ + char *rv; + ngx_str_t file; + + file.len = name->len + 4; + file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5); + if (file.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_sprintf(file.data, "%V.bin%Z", name); + + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ctx->ranges) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) { + case NGX_OK: + return NGX_CONF_OK; + case NGX_ERROR: + return NGX_CONF_ERROR; + default: + break; + } + } + + file.len -= 4; + file.data[file.len] = '\0'; + + ctx->include_name = file; + + if (ctx->outside_entries) { + ctx->allow_binary_include = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + rv = ngx_conf_parse(cf, &file); + + ctx->includes++; + ctx->outside_entries = 0; + + return rv; +} + + +static ngx_int_t +ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *name) +{ + u_char *base, ch; + time_t mtime; + size_t size, len; + ssize_t n; + uint32_t crc32; + ngx_err_t err; + ngx_int_t rc; + ngx_uint_t i; + ngx_file_t file; + ngx_file_info_t fi; + ngx_http_geo_range_t *range, **ranges; + ngx_http_geo_header_t *header; + ngx_http_variable_value_t *vv; + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.name = *name; + file.log = cf->log; + + file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0); + if (file.fd == NGX_INVALID_FILE) { + err = ngx_errno; + if (err != NGX_ENOENT) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, err, + ngx_open_file_n " \"%s\" failed", name->data); + } + return NGX_DECLINED; + } + + if (ctx->outside_entries) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "binary geo range base \"%s\" may not be mixed with usual entries", + name->data); + rc = NGX_ERROR; + goto done; + } + + if (ctx->binary_include) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "second binary geo range base \"%s\" may not be mixed with \"%s\"", + name->data, ctx->include_name.data); + rc = NGX_ERROR; + goto done; + } + + if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name->data); + goto failed; + } + + size = (size_t) ngx_file_size(&fi); + mtime = ngx_file_mtime(&fi); + + ch = name->data[name->len - 4]; + name->data[name->len - 4] = '\0'; + + if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_file_info_n " \"%s\" failed", name->data); + goto failed; + } + + name->data[name->len - 4] = ch; + + if (mtime < ngx_file_mtime(&fi)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "stale binary geo range base \"%s\"", name->data); + goto failed; + } + + base = ngx_palloc(ctx->pool, size); + if (base == NULL) { + goto failed; + } + + n = ngx_read_file(&file, base, size, 0); + + if (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_read_file_n " \"%s\" failed", name->data); + goto failed; + } + + if ((size_t) n != size) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, + ngx_read_file_n " \"%s\" returned only %z bytes instead of %z", + name->data, n, size); + goto failed; + } + + header = (ngx_http_geo_header_t *) base; + + if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "incompatible binary geo range base \"%s\"", name->data); + goto failed; + } + + ngx_crc32_init(crc32); + + vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t)); + + while(vv->data) { + len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len, + sizeof(void *)); + ngx_crc32_update(&crc32, (u_char *) vv, len); + vv->data += (size_t) base; + vv = (ngx_http_variable_value_t *) ((u_char *) vv + len); + } + ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t)); + vv++; + + ranges = (ngx_http_geo_range_t **) vv; + + for (i = 0; i < 0x10000; i++) { + ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *)); + if (ranges[i]) { + ranges[i] = (ngx_http_geo_range_t *) + ((u_char *) ranges[i] + (size_t) base); + } + } + + range = (ngx_http_geo_range_t *) &ranges[0x10000]; + + while ((u_char *) range < base + size) { + while (range->value) { + ngx_crc32_update(&crc32, (u_char *) range, + sizeof(ngx_http_geo_range_t)); + range->value = (ngx_http_variable_value_t *) + ((u_char *) range->value + (size_t) base); + range++; + } + ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *)); + range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *)); + } + + ngx_crc32_final(crc32); + + if (crc32 != header->crc32) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "CRC32 mismatch in binary geo range base \"%s\"", name->data); + goto failed; + } + + ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, + "using binary geo range base \"%s\"", name->data); + + ctx->include_name = *name; + ctx->binary_include = 1; + ctx->high.low = ranges; + rc = NGX_OK; + + goto done; + +failed: + + rc = NGX_DECLINED; + +done: + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name->data); + } + + return rc; +} + + +static void +ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx) +{ + u_char *p; + uint32_t hash; + ngx_str_t s; + ngx_uint_t i; + ngx_file_mapping_t fm; + ngx_http_geo_range_t *r, *range, **ranges; + ngx_http_geo_header_t *header; + ngx_http_geo_variable_value_node_t *gvvn; + + fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5); + if (fm.name == NULL) { + return; + } + + ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name); + + fm.size = ctx->data_size; + fm.log = ctx->pool->log; + + ngx_log_error(NGX_LOG_NOTICE, fm.log, 0, + "creating binary geo range base \"%s\"", fm.name); + + if (ngx_create_file_mapping(&fm) != NGX_OK) { + return; + } + + p = ngx_cpymem(fm.addr, &ngx_http_geo_header, + sizeof(ngx_http_geo_header_t)); + + p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root, + ctx->rbtree.sentinel); + + p += sizeof(ngx_http_variable_value_t); + + ranges = (ngx_http_geo_range_t **) p; + + p += 0x10000 * sizeof(ngx_http_geo_range_t *); + + for (i = 0; i < 0x10000; i++) { + r = ctx->high.low[i]; + if (r == NULL) { + continue; + } + + range = (ngx_http_geo_range_t *) p; + ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr); + + do { + s.len = r->value->len; + s.data = r->value->data; + hash = ngx_crc32_long(s.data, s.len); + gvvn = (ngx_http_geo_variable_value_node_t *) + ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash); + + range->value = (ngx_http_variable_value_t *) gvvn->offset; + range->start = r->start; + range->end = r->end; + range++; + + } while ((++r)->value); + + range->value = NULL; + + p = (u_char *) range + sizeof(void *); + } + + header = fm.addr; + header->crc32 = ngx_crc32_long((u_char *) fm.addr + + sizeof(ngx_http_geo_header_t), + fm.size - sizeof(ngx_http_geo_header_t)); + + ngx_close_file_mapping(&fm); +} + + +static u_char * +ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node, + ngx_rbtree_node_t *sentinel) +{ + ngx_http_variable_value_t *vv; + ngx_http_geo_variable_value_node_t *gvvn; + + if (node == sentinel) { + return p; + } + + gvvn = (ngx_http_geo_variable_value_node_t *) node; + gvvn->offset = p - base; + + vv = (ngx_http_variable_value_t *) p; + *vv = *gvvn->value; + p += sizeof(ngx_http_variable_value_t); + vv->data = (u_char *) (p - base); + + p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len); + + p = ngx_align_ptr(p, sizeof(void *)); + + p = ngx_http_geo_copy_values(base, p, node->left, sentinel); + + return ngx_http_geo_copy_values(base, p, node->right, sentinel); +}