<commit_before>/*
 * Copyright 2013 Luciad (http://www.luciad.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <stdio.h>
#include <string.h>
#include "wkb.h"
#include "sqlite.h"

#define WKB_BE 0
#define WKB_LE 1

#define WKB_XY 0
#define WKB_XYZ 1000
#define WKB_XYM 2000
#define WKB_XYZM 3000

#define WKB_POINT 1
#define WKB_LINESTRING 2
#define WKB_POLYGON 3
#define WKB_MULTIPOINT 4
#define WKB_MULTILINESTRING 5
#define WKB_MULTIPOLYGON 6
#define WKB_GEOMETRYCOLLECTION 7

#define MINMAXCOORD(coord) double coord = coords[off++]; \
  if (coord < env->min_ ## coord) { \
    env->min_ ## coord = coord; \
  } \
  if (coord > env->max_ ## coord) { \
      env->max_ ## coord = coord; \
  }

/** @private */
typedef struct {
    geom_consumer_t consumer;
    geom_envelope_t *header;
} fill_header_t;

static int fill_gpb_envelope_coordinates(const geom_consumer_t *consumer, const geom_header_t *header, size_t point_count, const double *coords) {
    geom_envelope_t *env = ((fill_header_t *) consumer)->header;
    if (header->coord_type == GEOM_XY) {
        env->has_env_x = 1;
        env->has_env_y = 1;
        for (int i = 0, off = 0; i < point_count; i++) {
            MINMAXCOORD(x)
            MINMAXCOORD(y)
        }
    } else if (header->coord_type == GEOM_XYZ) {
        env->has_env_x = 1;
        env->has_env_y = 1;
        env->has_env_z = 1;
        for (int i = 0, off = 0; i < point_count; i++) {
            MINMAXCOORD(x)
            MINMAXCOORD(y)
            MINMAXCOORD(z)
        }
    } else if (header->coord_type == GEOM_XYM) {
        env->has_env_x = 1;
        env->has_env_y = 1;
        env->has_env_m = 1;
        for (int i = 0, off = 0; i < point_count; i++) {
            MINMAXCOORD(x)
            MINMAXCOORD(y)
            MINMAXCOORD(m)
        }
    } else {
        env->has_env_x = 1;
        env->has_env_y = 1;
        env->has_env_z = 1;
        env->has_env_m = 1;
        for (int i = 0, off = 0; i < point_count; i++) {
            MINMAXCOORD(x)
            MINMAXCOORD(y)
            MINMAXCOORD(z)
            MINMAXCOORD(m)
        }
    }
    return SQLITE_OK;
}

int wkb_fill_envelope(binstream_t *stream, geom_envelope_t *envelope) {
    geom_envelope_init(envelope);

    fill_header_t fill_gpb;
    fill_gpb.header = envelope;
    geom_consumer_init(&fill_gpb.consumer, NULL, NULL, NULL, NULL, fill_gpb_envelope_coordinates);
    int result = wkb_read_geometry(stream, &fill_gpb.consumer);

    return result;
}

static int read_wkb_geometry_header(binstream_t *stream, geom_header_t *header) {
    uint8_t order;
    if (binstream_read_u8(stream, &order) != SQLITE_OK) {
        return SQLITE_IOERR;
    }
    binstream_set_endianness(stream, order == WKB_BE ? BIG : LITTLE);

    uint32_t type;
    if (binstream_read_u32(stream, &type) != SQLITE_OK) {
        return SQLITE_IOERR;
    }
    uint32_t modifier = (type / 1000) * 1000;
    type -= modifier * 1000;

    switch (modifier) {
        case WKB_XY:
            header->coord_size = 2;
            header->coord_type = GEOM_XY;
            break;
        case WKB_XYZ:
            header->coord_size = 3;
            header->coord_type = GEOM_XYZ;
            break;
        case WKB_XYM:
            header->coord_size = 3;
            header->coord_type = GEOM_XYM;
            break;
        case WKB_XYZM:
            header->coord_size = 4;
            header->coord_type = GEOM_XYZM;
            break;
        default:
            return SQLITE_IOERR;
    }
    
    switch (type) {
        case WKB_POINT:
            header->geom_type = GEOM_POINT;
            break;
        case WKB_LINESTRING:
            header->geom_type = GEOM_LINESTRING;
            break;
        case WKB_POLYGON:
            header->geom_type = GEOM_POLYGON;
            break;
        case WKB_MULTIPOINT:
            header->geom_type = GEOM_MULTIPOINT;
            break;
        case WKB_MULTILINESTRING:
            header->geom_type = GEOM_MULTILINESTRING;
            break;
        case WKB_MULTIPOLYGON:
            header->geom_type = GEOM_MULTIPOLYGON;
            break;
        case WKB_GEOMETRYCOLLECTION:
            header->geom_type = GEOM_GEOMETRYCOLLECTION;
            break;
        default:
            return SQLITE_IOERR;
    }

    return SQLITE_OK;
}

static int read_point(binstream_t *stream, const geom_consumer_t *consumer, const geom_header_t *header) {
    int result;
    uint32_t coord_size = header->coord_size;
    double coord[coord_size];
    for (int i = 0; i < coord_size; i++) {
        result = binstream_read_double(stream, &coord[i]);
        if (result != SQLITE_OK) {
            return result;
        }
    }

    return consumer->coordinates(consumer, header, 1, coord);
}

#define COORD_BATCH_SIZE 10

static int read_points(binstream_t *stream, const geom_consumer_t *consumer, const geom_header_t *header, uint32_t point_count) {
    int result;
    double coord[header->coord_size * COORD_BATCH_SIZE];

    uint32_t remaining = point_count;
    while(remaining > 0) {
        uint32_t points_to_read = (remaining > COORD_BATCH_SIZE ? COORD_BATCH_SIZE : remaining);
        uint32_t coords_to_read = points_to_read * header->coord_size;
        for (int i = 0; i < coords_to_read; i++) {
            result = binstream_read_double(stream, &coord[i]);
            if (result != SQLITE_OK) {
                return result;
            }
        }

        result = consumer->coordinates(consumer, header, points_to_read, coord);
        if (result != SQLITE_OK) {
            return result;
        }
    }

    return SQLITE_OK;
}

static int read_linearring(binstream_t *stream, const geom_consumer_t *consumer, const geom_header_t *header) {
    int result = SQLITE_OK;
<commit_msg>wkb.c: remaining was not decreased in read_points<commit_after>/*
 * Copyright 2013 Luciad (http://www.luciad.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <stdio.h>
#include <string.h>
#include "wkb.h"
#include "sqlite.h"

#define WKB_BE 0
#define WKB_LE 1

#define WKB_XY 0
#define WKB_XYZ 1000
#define WKB_XYM 2000
#define WKB_XYZM 3000

#define WKB_POINT 1
#define WKB_LINESTRING 2
#define WKB_POLYGON 3
#define WKB_MULTIPOINT 4
#define WKB_MULTILINESTRING 5
#define WKB_MULTIPOLYGON 6
#define WKB_GEOMETRYCOLLECTION 7

#define MINMAXCOORD(coord) double coord = coords[off++]; \
  if (coord < env->min_ ## coord) { \
    env->min_ ## coord = coord; \
  } \
  if (coord > env->max_ ## coord) { \
      env->max_ ## coord = coord; \
  }

/** @private */
typedef struct {
    geom_consumer_t consumer;
    geom_envelope_t *header;
} fill_header_t;

static int fill_gpb_envelope_coordinates(const geom_consumer_t *consumer, const geom_header_t *header, size_t point_count, const double *coords) {
    geom_envelope_t *env = ((fill_header_t *) consumer)->header;
    if (header->coord_type == GEOM_XY) {
        env->has_env_x = 1;
        env->has_env_y = 1;
        for (int i = 0, off = 0; i < point_count; i++) {
            MINMAXCOORD(x)
            MINMAXCOORD(y)
        }
    } else if (header->coord_type == GEOM_XYZ) {
        env->has_env_x = 1;
        env->has_env_y = 1;
        env->has_env_z = 1;
        for (int i = 0, off = 0; i < point_count; i++) {
            MINMAXCOORD(x)
            MINMAXCOORD(y)
            MINMAXCOORD(z)
        }
    } else if (header->coord_type == GEOM_XYM) {
        env->has_env_x = 1;
        env->has_env_y = 1;
        env->has_env_m = 1;
        for (int i = 0, off = 0; i < point_count; i++) {
            MINMAXCOORD(x)
            MINMAXCOORD(y)
            MINMAXCOORD(m)
        }
    } else {
        env->has_env_x = 1;
        env->has_env_y = 1;
        env->has_env_z = 1;
        env->has_env_m = 1;
        for (int i = 0, off = 0; i < point_count; i++) {
            MINMAXCOORD(x)
            MINMAXCOORD(y)
            MINMAXCOORD(z)
            MINMAXCOORD(m)
        }
    }
    return SQLITE_OK;
}

int wkb_fill_envelope(binstream_t *stream, geom_envelope_t *envelope) {
    geom_envelope_init(envelope);

    fill_header_t fill_gpb;
    fill_gpb.header = envelope;
    geom_consumer_init(&fill_gpb.consumer, NULL, NULL, NULL, NULL, fill_gpb_envelope_coordinates);
    int result = wkb_read_geometry(stream, &fill_gpb.consumer);

    return result;
}

static int read_wkb_geometry_header(binstream_t *stream, geom_header_t *header) {
    uint8_t order;
    if (binstream_read_u8(stream, &order) != SQLITE_OK) {
        return SQLITE_IOERR;
    }
    binstream_set_endianness(stream, order == WKB_BE ? BIG : LITTLE);

    uint32_t type;
    if (binstream_read_u32(stream, &type) != SQLITE_OK) {
        return SQLITE_IOERR;
    }
    uint32_t modifier = (type / 1000) * 1000;
    type -= modifier * 1000;

    switch (modifier) {
        case WKB_XY:
            header->coord_size = 2;
            header->coord_type = GEOM_XY;
            break;
        case WKB_XYZ:
            header->coord_size = 3;
            header->coord_type = GEOM_XYZ;
            break;
        case WKB_XYM:
            header->coord_size = 3;
            header->coord_type = GEOM_XYM;
            break;
        case WKB_XYZM:
            header->coord_size = 4;
            header->coord_type = GEOM_XYZM;
            break;
        default:
            return SQLITE_IOERR;
    }
    
    switch (type) {
        case WKB_POINT:
            header->geom_type = GEOM_POINT;
            break;
        case WKB_LINESTRING:
            header->geom_type = GEOM_LINESTRING;
            break;
        case WKB_POLYGON:
            header->geom_type = GEOM_POLYGON;
            break;
        case WKB_MULTIPOINT:
            header->geom_type = GEOM_MULTIPOINT;
            break;
        case WKB_MULTILINESTRING:
            header->geom_type = GEOM_MULTILINESTRING;
            break;
        case WKB_MULTIPOLYGON:
            header->geom_type = GEOM_MULTIPOLYGON;
            break;
        case WKB_GEOMETRYCOLLECTION:
            header->geom_type = GEOM_GEOMETRYCOLLECTION;
            break;
        default:
            return SQLITE_IOERR;
    }

    return SQLITE_OK;
}

static int read_point(binstream_t *stream, const geom_consumer_t *consumer, const geom_header_t *header) {
    int result;
    uint32_t coord_size = header->coord_size;
    double coord[coord_size];
    for (int i = 0; i < coord_size; i++) {
        result = binstream_read_double(stream, &coord[i]);
        if (result != SQLITE_OK) {
            return result;
        }
    }

    return consumer->coordinates(consumer, header, 1, coord);
}

#define COORD_BATCH_SIZE 10

static int read_points(binstream_t *stream, const geom_consumer_t *consumer, const geom_header_t *header, uint32_t point_count) {
    int result;
    double coord[header->coord_size * COORD_BATCH_SIZE];

    uint32_t remaining = point_count;
    while(remaining > 0) {
        uint32_t points_to_read = (remaining > COORD_BATCH_SIZE ? COORD_BATCH_SIZE : remaining);
        uint32_t coords_to_read = points_to_read * header->coord_size;
        for (int i = 0; i < coords_to_read; i++) {
            result = binstream_read_double(stream, &coord[i]);
            if (result != SQLITE_OK) {
                return result;
            }
        }

        result = consumer->coordinates(consumer, header, points_to_read, coord);
        if (result != SQLITE_OK) {
            return result;
        }

        remaining -= points_to_read;
    }

    return SQLITE_OK;
}

static int read_linearring(binstream_t *stream, const geom_consumer_t *consumer, const geom_header_t *header) {
    int result = SQLITE_OK;

{'fraction_non_alphanumeric': 0.09841646872525732, 'fraction_numerical': 0.014251781472684086, 'mean_word_length': 2.3300817295017135, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 93, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6289150', 'n_tokens_mistral': 4619, 'n_tokens_neox': 4285, 'n_words': 1016}