// https://github.com/tidwall/tg // // Copyright 2023 Joshua J Baker. All rights reserved. // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include #include #include /****************************************************************************** Implementation Notes: - Right-hand-rule is not required for polygons. - The "properties" GeoJSON field is not required for Features. - All polygons with at least 32 points are indexed automatically (TG_NATURAL). Developer notes: - This tg.c source file includes the entire tg library. (amalgamation) - All dependencies are embedded between the BEGIN/END tags. - Do not edit a dependency directly in this file. Instead edit the file in the deps directory and then run deps/embed.sh to replace out its code in this file. *******************************************************************************/ #ifdef TG_STATIC #define TG_EXTERN static #endif #ifndef TG_EXTERN #define TG_EXTERN #endif struct tg_point { double x; double y; }; struct tg_segment { struct tg_point a; struct tg_point b; }; struct tg_rect { struct tg_point min; struct tg_point max; }; struct tg_line; ///< Find the description in the tg.c file. struct tg_ring; ///< Find the description in the tg.c file. struct tg_poly; ///< Find the description in the tg.c file. struct tg_geom; ///< Find the description in the tg.c file. enum tg_geom_type { TG_POINT = 1, ///< Point TG_LINESTRING = 2, ///< LineString TG_POLYGON = 3, ///< Polygon TG_MULTIPOINT = 4, ///< MultiPoint, collection of points TG_MULTILINESTRING = 5, ///< MultiLineString, collection of linestrings TG_MULTIPOLYGON = 6, ///< MultiPolygon, collection of polygons TG_GEOMETRYCOLLECTION = 7, ///< GeometryCollection, collection of geometries }; enum tg_index { TG_DEFAULT, ///< default is TG_NATURAL or tg_env_set_default_index(). TG_NONE, ///< no indexing available, or disabled. TG_NATURAL, ///< indexing with natural ring order, for rings/lines TG_YSTRIPES, ///< indexing using segment striping, rings only }; enum tg_raycast_result { TG_OUT, // point is above, below, or to the right of the segment TG_IN, // point is to the left (and inside the vertical bounds) TG_ON, // point is on the segment }; // Public methods. These should also be in the tg.h file TG_EXTERN struct tg_geom *tg_geom_new_point(struct tg_point point); TG_EXTERN struct tg_geom *tg_geom_new_linestring(const struct tg_line *line); TG_EXTERN struct tg_geom *tg_geom_new_polygon(const struct tg_poly *poly); TG_EXTERN struct tg_geom *tg_geom_new_multipoint(const struct tg_point *points, int npoints); TG_EXTERN struct tg_geom *tg_geom_new_multilinestring( const struct tg_line *const lines[], int nlines); TG_EXTERN struct tg_geom *tg_geom_new_multipolygon( const struct tg_poly *const polys[], int npolys); TG_EXTERN struct tg_geom *tg_geom_new_geometrycollection( const struct tg_geom *const geoms[], int ngeoms); TG_EXTERN struct tg_geom *tg_geom_new_error(const char *errmsg); TG_EXTERN struct tg_geom *tg_geom_clone(const struct tg_geom *geom); TG_EXTERN struct tg_geom *tg_geom_copy(const struct tg_geom *geom); TG_EXTERN void tg_geom_free(struct tg_geom *geom); TG_EXTERN enum tg_geom_type tg_geom_typeof(const struct tg_geom *geom); TG_EXTERN const char *tg_geom_type_string(enum tg_geom_type type); TG_EXTERN struct tg_rect tg_geom_rect(const struct tg_geom *geom); TG_EXTERN bool tg_geom_is_feature(const struct tg_geom *geom); TG_EXTERN bool tg_geom_is_featurecollection(const struct tg_geom *geom); TG_EXTERN struct tg_point tg_geom_point(const struct tg_geom *geom); TG_EXTERN const struct tg_line *tg_geom_line(const struct tg_geom *geom); TG_EXTERN const struct tg_poly *tg_geom_poly(const struct tg_geom *geom); TG_EXTERN int tg_geom_num_points(const struct tg_geom *geom); TG_EXTERN struct tg_point tg_geom_point_at(const struct tg_geom *geom, int index); TG_EXTERN int tg_geom_num_lines(const struct tg_geom *geom); TG_EXTERN const struct tg_line *tg_geom_line_at(const struct tg_geom *geom, int index); TG_EXTERN int tg_geom_num_polys(const struct tg_geom *geom); TG_EXTERN const struct tg_poly *tg_geom_poly_at(const struct tg_geom *geom, int index); TG_EXTERN int tg_geom_num_geometries(const struct tg_geom *geom); TG_EXTERN const struct tg_geom *tg_geom_geometry_at(const struct tg_geom *geom, int index); TG_EXTERN const char *tg_geom_extra_json(const struct tg_geom *geom); TG_EXTERN bool tg_geom_is_empty(const struct tg_geom *geom); TG_EXTERN int tg_geom_dims(const struct tg_geom *geom); TG_EXTERN bool tg_geom_has_z(const struct tg_geom *geom); TG_EXTERN bool tg_geom_has_m(const struct tg_geom *geom); TG_EXTERN double tg_geom_z(const struct tg_geom *geom); TG_EXTERN double tg_geom_m(const struct tg_geom *geom); TG_EXTERN const double *tg_geom_extra_coords(const struct tg_geom *geom); TG_EXTERN int tg_geom_num_extra_coords(const struct tg_geom *geom); TG_EXTERN size_t tg_geom_memsize(const struct tg_geom *geom); TG_EXTERN void tg_geom_search(const struct tg_geom *geom, struct tg_rect rect, bool (*iter)(const struct tg_geom *geom, int index, void *udata), void *udata); TG_EXTERN int tg_geom_fullrect(const struct tg_geom *geom, double min[4], double max[4]); TG_EXTERN bool tg_geom_equals(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_geom_intersects(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_geom_disjoint(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_geom_contains(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_geom_within(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_geom_covers(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_geom_coveredby(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_geom_touches(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_geom_intersects_rect(const struct tg_geom *a, struct tg_rect b); TG_EXTERN bool tg_geom_intersects_xy(const struct tg_geom *a, double x, double y); TG_EXTERN struct tg_geom *tg_parse_geojson(const char *geojson); TG_EXTERN struct tg_geom *tg_parse_geojsonn(const char *geojson, size_t len); TG_EXTERN struct tg_geom *tg_parse_geojson_ix(const char *geojson, enum tg_index ix); TG_EXTERN struct tg_geom *tg_parse_geojsonn_ix(const char *geojson, size_t len, enum tg_index ix); TG_EXTERN struct tg_geom *tg_parse_wkt(const char *wkt); TG_EXTERN struct tg_geom *tg_parse_wktn(const char *wkt, size_t len); TG_EXTERN struct tg_geom *tg_parse_wkt_ix(const char *wkt, enum tg_index ix); TG_EXTERN struct tg_geom *tg_parse_wktn_ix(const char *wkt, size_t len, enum tg_index ix); TG_EXTERN struct tg_geom *tg_parse_wkb(const uint8_t *wkb, size_t len); TG_EXTERN struct tg_geom *tg_parse_wkb_ix(const uint8_t *wkb, size_t len, enum tg_index ix); TG_EXTERN struct tg_geom *tg_parse_hex(const char *hex); TG_EXTERN struct tg_geom *tg_parse_hexn(const char *hex, size_t len); TG_EXTERN struct tg_geom *tg_parse_hex_ix(const char *hex, enum tg_index ix); TG_EXTERN struct tg_geom *tg_parse_hexn_ix(const char *hex, size_t len, enum tg_index ix); TG_EXTERN struct tg_geom *tg_parse_geobin(const uint8_t *geobin, size_t len); TG_EXTERN struct tg_geom *tg_parse_geobin_ix(const uint8_t *geobin, size_t len, enum tg_index ix); TG_EXTERN struct tg_geom *tg_parse(const void *data, size_t len); TG_EXTERN struct tg_geom *tg_parse_ix(const void *data, size_t len, enum tg_index ix); TG_EXTERN const char *tg_geom_error(const struct tg_geom *geom); TG_EXTERN int tg_geobin_fullrect(const uint8_t *geobin, size_t len, double min[4], double max[4]); TG_EXTERN struct tg_rect tg_geobin_rect(const uint8_t *geobin, size_t len); TG_EXTERN struct tg_point tg_geobin_point(const uint8_t *geobin, size_t len); TG_EXTERN size_t tg_geom_geojson(const struct tg_geom *geom, char *dst, size_t n); TG_EXTERN size_t tg_geom_wkt(const struct tg_geom *geom, char *dst, size_t n); TG_EXTERN size_t tg_geom_wkb(const struct tg_geom *geom, uint8_t *dst, size_t n); TG_EXTERN size_t tg_geom_hex(const struct tg_geom *geom, char *dst, size_t n); TG_EXTERN size_t tg_geom_geobin(const struct tg_geom *geom, uint8_t *dst, size_t n); TG_EXTERN struct tg_geom *tg_geom_new_point_z(struct tg_point point, double z); TG_EXTERN struct tg_geom *tg_geom_new_point_m(struct tg_point point, double m); TG_EXTERN struct tg_geom *tg_geom_new_point_zm(struct tg_point point, double z, double m); TG_EXTERN struct tg_geom *tg_geom_new_point_empty(void); TG_EXTERN struct tg_geom *tg_geom_new_linestring_z(const struct tg_line *line, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_linestring_m(const struct tg_line *line, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_linestring_zm(const struct tg_line *line, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_linestring_empty(void); TG_EXTERN struct tg_geom *tg_geom_new_polygon_z(const struct tg_poly *poly, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_polygon_m(const struct tg_poly *poly, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_polygon_zm(const struct tg_poly *poly, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_polygon_empty(void); TG_EXTERN struct tg_geom *tg_geom_new_multipoint_z( const struct tg_point *points, int npoints, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multipoint_m( const struct tg_point *points, int npoints, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multipoint_zm( const struct tg_point *points, int npoints, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multipoint_empty(void); TG_EXTERN struct tg_geom *tg_geom_new_multilinestring_z( const struct tg_line *const lines[], int nlines, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multilinestring_m( const struct tg_line *const lines[], int nlines, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multilinestring_zm( const struct tg_line *const lines[], int nlines, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multilinestring_empty(void); TG_EXTERN struct tg_geom *tg_geom_new_multipolygon_z( const struct tg_poly *const polys[], int npolys, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multipolygon_m( const struct tg_poly *const polys[], int npolys, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multipolygon_zm( const struct tg_poly *const polys[], int npolys, const double *extra_coords, int ncoords); TG_EXTERN struct tg_geom *tg_geom_new_multipolygon_empty(void); TG_EXTERN struct tg_geom *tg_geom_new_geometrycollection_empty(void); TG_EXTERN struct tg_rect tg_point_rect(struct tg_point point); TG_EXTERN bool tg_point_intersects_rect(struct tg_point a, struct tg_rect b); TG_EXTERN struct tg_rect tg_segment_rect(struct tg_segment s); TG_EXTERN bool tg_segment_intersects_segment(struct tg_segment a, struct tg_segment b); TG_EXTERN struct tg_rect tg_rect_expand(struct tg_rect rect, struct tg_rect other); TG_EXTERN struct tg_rect tg_rect_expand_point(struct tg_rect rect, struct tg_point point); TG_EXTERN struct tg_point tg_rect_center(struct tg_rect rect); TG_EXTERN bool tg_rect_intersects_rect(struct tg_rect a, struct tg_rect b); TG_EXTERN bool tg_rect_intersects_point(struct tg_rect a, struct tg_point b); TG_EXTERN struct tg_ring *tg_ring_new(const struct tg_point *points, int npoints); TG_EXTERN struct tg_ring *tg_ring_new_ix(const struct tg_point *points, int npoints, enum tg_index ix); TG_EXTERN void tg_ring_free(struct tg_ring *ring); TG_EXTERN struct tg_ring *tg_ring_clone(const struct tg_ring *ring); TG_EXTERN struct tg_ring *tg_ring_copy(const struct tg_ring *ring); TG_EXTERN size_t tg_ring_memsize(const struct tg_ring *ring); TG_EXTERN struct tg_rect tg_ring_rect(const struct tg_ring *ring); TG_EXTERN int tg_ring_num_points(const struct tg_ring *ring); TG_EXTERN struct tg_point tg_ring_point_at(const struct tg_ring *ring, int index); TG_EXTERN const struct tg_point *tg_ring_points(const struct tg_ring *ring); TG_EXTERN int tg_ring_num_segments(const struct tg_ring *ring); TG_EXTERN struct tg_segment tg_ring_segment_at(const struct tg_ring *ring, int index); TG_EXTERN bool tg_ring_convex(const struct tg_ring *ring); TG_EXTERN bool tg_ring_clockwise(const struct tg_ring *ring); TG_EXTERN int tg_ring_index_spread(const struct tg_ring *ring); TG_EXTERN int tg_ring_index_num_levels(const struct tg_ring *ring); TG_EXTERN int tg_ring_index_level_num_rects(const struct tg_ring *ring, int levelidx); TG_EXTERN struct tg_rect tg_ring_index_level_rect(const struct tg_ring *ring, int levelidx, int rectidx); TG_EXTERN bool tg_ring_nearest_segment(const struct tg_ring *ring, double (*rect_dist)(struct tg_rect rect, int *more, void *udata), double (*seg_dist)(struct tg_segment seg, int *more, void *udata), bool (*iter)(struct tg_segment seg, double dist, int index, void *udata), void *udata); TG_EXTERN void tg_ring_line_search(const struct tg_ring *a, const struct tg_line *b, bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg, int bidx, void *udata), void *udata); TG_EXTERN void tg_ring_ring_search(const struct tg_ring *a, const struct tg_ring *b, bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg, int bidx, void *udata), void *udata); TG_EXTERN double tg_ring_area(const struct tg_ring *ring); TG_EXTERN double tg_ring_perimeter(const struct tg_ring *ring); TG_EXTERN struct tg_line *tg_line_new(const struct tg_point *points, int npoints); TG_EXTERN struct tg_line *tg_line_new_ix(const struct tg_point *points, int npoints, enum tg_index ix); TG_EXTERN void tg_line_free(struct tg_line *line); TG_EXTERN struct tg_line *tg_line_clone(const struct tg_line *line); TG_EXTERN struct tg_line *tg_line_copy(const struct tg_line *line); TG_EXTERN size_t tg_line_memsize(const struct tg_line *line); TG_EXTERN struct tg_rect tg_line_rect(const struct tg_line *line); TG_EXTERN int tg_line_num_points(const struct tg_line *line); TG_EXTERN const struct tg_point *tg_line_points(const struct tg_line *line); TG_EXTERN struct tg_point tg_line_point_at(const struct tg_line *line, int index); TG_EXTERN int tg_line_num_segments(const struct tg_line *line); TG_EXTERN struct tg_segment tg_line_segment_at(const struct tg_line *line, int index); TG_EXTERN bool tg_line_clockwise(const struct tg_line *line); TG_EXTERN int tg_line_index_spread(const struct tg_line *line); TG_EXTERN int tg_line_index_num_levels(const struct tg_line *line); TG_EXTERN int tg_line_index_level_num_rects(const struct tg_line *line, int levelidx); TG_EXTERN struct tg_rect tg_line_index_level_rect(const struct tg_line *line, int levelidx, int rectidx); TG_EXTERN bool tg_line_nearest_segment(const struct tg_line *line, double (*rect_dist)(struct tg_rect rect, int *more, void *udata), double (*seg_dist)(struct tg_segment seg, int *more, void *udata), bool (*iter)(struct tg_segment seg, double dist, int index, void *udata), void *udata); TG_EXTERN void tg_line_line_search(const struct tg_line *a, const struct tg_line *b, bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg, int bidx, void *udata), void *udata); TG_EXTERN double tg_line_length(const struct tg_line *line); TG_EXTERN struct tg_poly *tg_poly_new(const struct tg_ring *exterior, const struct tg_ring *const holes[], int nholes); TG_EXTERN void tg_poly_free(struct tg_poly *poly); TG_EXTERN struct tg_poly *tg_poly_clone(const struct tg_poly *poly); TG_EXTERN struct tg_poly *tg_poly_copy(const struct tg_poly *poly); TG_EXTERN size_t tg_poly_memsize(const struct tg_poly *poly); TG_EXTERN const struct tg_ring *tg_poly_exterior(const struct tg_poly *poly); TG_EXTERN int tg_poly_num_holes(const struct tg_poly *poly); TG_EXTERN const struct tg_ring *tg_poly_hole_at(const struct tg_poly *poly, int index); TG_EXTERN struct tg_rect tg_poly_rect(const struct tg_poly *poly); TG_EXTERN bool tg_poly_clockwise(const struct tg_poly *poly); TG_EXTERN void tg_env_set_allocator(void *(*malloc)(size_t), void *(*realloc)(void*, size_t), void (*free)(void*)); TG_EXTERN void tg_env_set_index(enum tg_index ix); TG_EXTERN void tg_env_set_index_spread(int spread); TG_EXTERN void tg_env_set_print_fixed_floats(bool print); // Private methods. Not listed in the tg.h file. TG_EXTERN bool tg_ring_empty(const struct tg_ring *ring); TG_EXTERN bool tg_line_empty(const struct tg_line *line); TG_EXTERN bool tg_poly_empty(const struct tg_poly *poly); TG_EXTERN void tg_rect_search(struct tg_rect rect, struct tg_rect target, bool(*iter)(struct tg_segment seg, int index, void *udata), void *udata); TG_EXTERN void tg_ring_search(const struct tg_ring *ring, struct tg_rect rect, bool(*iter)(struct tg_segment seg, int index, void *udata), void *udata); TG_EXTERN void tg_line_search(const struct tg_line *ring, struct tg_rect rect, bool(*iter)(struct tg_segment seg, int index, void *udata), void *udata); TG_EXTERN void tg_geom_foreach(const struct tg_geom *geom, bool(*iter)( const struct tg_geom *geom, void *udata), void *udata); TG_EXTERN double tg_ring_polsby_popper_score(const struct tg_ring *ring); TG_EXTERN double tg_line_polsby_popper_score(const struct tg_line *line); TG_EXTERN int tg_rect_num_points(struct tg_rect rect); TG_EXTERN struct tg_point tg_rect_point_at(struct tg_rect rect, int index); TG_EXTERN int tg_rect_num_segments(struct tg_rect rect); TG_EXTERN struct tg_segment tg_rect_segment_at(struct tg_rect rect, int index); TG_EXTERN int tg_geom_de9im_dims(const struct tg_geom *geom); TG_EXTERN bool tg_point_covers_point(struct tg_point a, struct tg_point b); TG_EXTERN bool tg_point_covers_rect(struct tg_point a, struct tg_rect b); TG_EXTERN bool tg_point_covers_line(struct tg_point a, const struct tg_line *b); TG_EXTERN bool tg_point_covers_poly(struct tg_point a, const struct tg_poly *b); TG_EXTERN bool tg_geom_covers_point(const struct tg_geom *a, struct tg_point b); TG_EXTERN bool tg_geom_covers_xy(const struct tg_geom *a, double x, double y); TG_EXTERN bool tg_segment_covers_segment(struct tg_segment a, struct tg_segment b); TG_EXTERN bool tg_segment_covers_point(struct tg_segment a, struct tg_point b); TG_EXTERN bool tg_segment_covers_rect(struct tg_segment a, struct tg_rect b); TG_EXTERN bool tg_rect_covers_point(struct tg_rect a, struct tg_point b); TG_EXTERN bool tg_rect_covers_xy(struct tg_rect a, double x, double y); TG_EXTERN bool tg_rect_covers_rect(struct tg_rect a, struct tg_rect b); TG_EXTERN bool tg_rect_covers_line(struct tg_rect a, const struct tg_line *b); TG_EXTERN bool tg_rect_covers_poly(struct tg_rect a, const struct tg_poly *b); TG_EXTERN bool tg_line_covers_point(const struct tg_line *a, struct tg_point b); TG_EXTERN bool tg_line_covers_rect(const struct tg_line *a, struct tg_rect b); TG_EXTERN bool tg_line_covers_line(const struct tg_line *a, const struct tg_line *b); TG_EXTERN bool tg_line_covers_poly(const struct tg_line *a, const struct tg_poly *b); TG_EXTERN bool tg_line_intersects_point(const struct tg_line *a, struct tg_point b); TG_EXTERN bool tg_line_intersects_rect(const struct tg_line *a, struct tg_rect b); TG_EXTERN bool tg_line_intersects_line(const struct tg_line *a, const struct tg_line *b); TG_EXTERN bool tg_line_intersects_poly(const struct tg_line *a, const struct tg_poly *b); TG_EXTERN bool tg_point_intersects_point(struct tg_point a, struct tg_point b); TG_EXTERN bool tg_point_intersects_rect(struct tg_point a, struct tg_rect b); TG_EXTERN bool tg_point_intersects_line(struct tg_point a, const struct tg_line *b); TG_EXTERN bool tg_point_intersects_poly(struct tg_point a, const struct tg_poly *b); TG_EXTERN bool tg_rect_intersects_rect(struct tg_rect a, struct tg_rect b); TG_EXTERN bool tg_rect_intersects_line(struct tg_rect a, const struct tg_line *b); TG_EXTERN bool tg_rect_intersects_poly(struct tg_rect a, const struct tg_poly *b); TG_EXTERN bool tg_segment_intersects_segment(struct tg_segment a, struct tg_segment b); TG_EXTERN bool tg_poly_covers_xy(const struct tg_poly *a, double x, double y); TG_EXTERN bool tg_poly_touches_line(const struct tg_poly *a, const struct tg_line *b); TG_EXTERN bool tg_poly_covers_point(const struct tg_poly *a, struct tg_point b); TG_EXTERN bool tg_poly_covers_rect(const struct tg_poly *a, struct tg_rect b); TG_EXTERN bool tg_poly_covers_line(const struct tg_poly *a, const struct tg_line *b); TG_EXTERN bool tg_poly_covers_poly(const struct tg_poly *a, const struct tg_poly *b); TG_EXTERN bool tg_poly_intersects_point(const struct tg_poly *a, struct tg_point b); TG_EXTERN bool tg_poly_intersects_rect(const struct tg_poly *a, struct tg_rect b); TG_EXTERN bool tg_poly_intersects_line(const struct tg_poly *a, const struct tg_line *b); TG_EXTERN bool tg_poly_intersects_poly(const struct tg_poly *a, const struct tg_poly *b); TG_EXTERN bool tg_geom_intersects_point(const struct tg_geom *a, struct tg_point b); TG_EXTERN struct tg_segment tg_segment_move(struct tg_segment seg, double delta_x, double delta_y); TG_EXTERN struct tg_ring *tg_ring_move(const struct tg_ring *ring, double delta_x, double delta_y); TG_EXTERN bool tg_ring_intersects_segment(const struct tg_ring *ring, struct tg_segment seg, bool allow_on_edge); TG_EXTERN enum tg_raycast_result tg_raycast(struct tg_segment seg, struct tg_point p); TG_EXTERN void *tg_realloc(void *ptr, size_t nbytes); TG_EXTERN void *tg_malloc(size_t nbytes); TG_EXTERN void tg_free(void *ptr); TG_EXTERN bool tg_point_touches_poly(struct tg_point point, const struct tg_poly *poly); TG_EXTERN bool tg_poly_contains_geom(struct tg_poly *a, const struct tg_geom *b); TG_EXTERN bool tg_poly_contains_line(const struct tg_poly *a, const struct tg_line *b); TG_EXTERN bool tg_poly_contains_point(const struct tg_poly *poly, struct tg_point point); TG_EXTERN bool tg_poly_contains_poly(const struct tg_poly *a, const struct tg_poly *b); TG_EXTERN struct tg_poly *tg_poly_move(const struct tg_poly *poly, double delta_x, double delta_y); TG_EXTERN bool tg_poly_touches_poly(const struct tg_poly *a, const struct tg_poly *b); TG_EXTERN double tg_rect_distance_rect(struct tg_rect a, struct tg_rect b); TG_EXTERN struct tg_rect tg_rect_move(struct tg_rect rect, double delta_x, double delta_y); TG_EXTERN bool tg_ring_contains_line(const struct tg_ring *a, const struct tg_line *b, bool allow_on_edge, bool respect_boundaries); TG_EXTERN struct ring_result tg_ring_contains_point(const struct tg_ring *ring, struct tg_point point, bool allow_on_edge); TG_EXTERN double tg_point_distance_point(struct tg_point a, struct tg_point b); TG_EXTERN double tg_point_distance_rect(struct tg_point p, struct tg_rect r); TG_EXTERN double tg_point_distance_segment(struct tg_point p, struct tg_segment s); TG_EXTERN uint32_t tg_point_hilbert(struct tg_point point, struct tg_rect rect); TG_EXTERN struct tg_point tg_point_move(struct tg_point point, double delta_x, double delta_y); TG_EXTERN bool tg_point_touches_geom(struct tg_point a, const struct tg_geom *b); TG_EXTERN bool tg_point_touches_line(struct tg_point point, const struct tg_line *line); TG_EXTERN struct tg_line *tg_line_move(const struct tg_line *line, double delta_x, double delta_y); TG_EXTERN bool tg_line_touches_geom(struct tg_line *a, const struct tg_geom *b); TG_EXTERN bool tg_line_touches_line(const struct tg_line *a, const struct tg_line *b); TG_EXTERN bool tg_line_touches_point(const struct tg_line *line, struct tg_point point); TG_EXTERN bool tg_line_touches_poly(const struct tg_line *a, const struct tg_poly *b); TG_EXTERN bool tg_point_contains_geom(struct tg_point a, const struct tg_geom *b); TG_EXTERN bool tg_point_contains_line(struct tg_point point, const struct tg_line *line); TG_EXTERN void tg_geom_setnoheap(struct tg_geom *geom); TG_EXTERN enum tg_index tg_index_extract_spread(enum tg_index ix, int *spread); TG_EXTERN enum tg_index tg_index_with_spread(enum tg_index ix, int spread); TG_EXTERN bool tg_line_contains_geom(struct tg_line *a, const struct tg_geom *b); TG_EXTERN bool tg_line_contains_line(const struct tg_line *line, const struct tg_line *other); TG_EXTERN bool tg_line_contains_point(const struct tg_line *line, struct tg_point point); TG_EXTERN bool tg_line_contains_poly(const struct tg_line *line, const struct tg_poly *poly); TG_EXTERN bool tg_point_contains_point(struct tg_point point, struct tg_point other); TG_EXTERN int tg_geom_multi_index_spread(const struct tg_geom *geom); TG_EXTERN bool tg_geom_overlaps(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN bool tg_point_contains_poly(struct tg_point point, const struct tg_poly *poly); TG_EXTERN bool tg_point_touches_point(struct tg_point a, struct tg_point b); TG_EXTERN bool tg_poly_touches_geom(struct tg_poly *a, const struct tg_geom *b); TG_EXTERN bool tg_poly_touches_point(const struct tg_poly *poly, struct tg_point point); TG_EXTERN size_t tg_aligned_size(size_t size); TG_EXTERN struct tg_ring *tg_circle_new_ix(struct tg_point center, double radius, int steps, enum tg_index ix); TG_EXTERN enum tg_index tg_env_get_default_index(void); TG_EXTERN int tg_env_get_index_spread(void); TG_EXTERN bool tg_geom_crosses(const struct tg_geom *a, const struct tg_geom *b); TG_EXTERN int tg_geom_multi_index_level_num_rects(const struct tg_geom *geom, int levelidx); TG_EXTERN struct tg_rect tg_geom_multi_index_level_rect( const struct tg_geom *geom, int levelidx, int rectidx); TG_EXTERN struct tg_ring *tg_circle_new(struct tg_point center, double radius, int steps); TG_EXTERN int tg_geom_multi_index_num_levels(const struct tg_geom *geom); TG_EXTERN bool tg_ring_contains_ring(const struct tg_ring *a, const struct tg_ring *b, bool allow_on_edge); TG_EXTERN bool tg_ring_contains_segment(const struct tg_ring *ring, struct tg_segment seg, bool allow_on_edge); TG_EXTERN bool tg_ring_intersects_line(const struct tg_ring *ring, const struct tg_line *line, bool allow_on_edge); TG_EXTERN bool tg_ring_intersects_ring(const struct tg_ring *ring, const struct tg_ring *other, bool allow_on_edge); enum base { BASE_POINT = 1, // tg_point BASE_LINE = 2, // tg_line BASE_RING = 3, // tg_ring BASE_POLY = 4, // tg_poly BASE_GEOM = 5, // tg_geom }; enum flags { HAS_Z = 1<<0, // Geometry has additional Z coordinates HAS_M = 1<<1, // Geometry has additional M coordinates IS_ERROR = 1<<2, // Geometry is a parse error. Falls back to POINT IS_EMPTY = 1<<3, // Same as a GeoJSON null object (empty coords) IS_FEATURE = 1<<4, // GeoJSON. Geometry is Feature IS_FEATURE_COL = 1<<5, // GeoJSON. GeometryGollection is FeatureCollection HAS_NULL_PROPS = 1<<6, // GeoJSON. 'Feature' with 'properties'=null IS_UNLOCATED = 1<<7, // GeoJSON. 'Feature' with 'geometry'=null }; // Optionally use non-atomic reference counting when TG_NOATOMICS is defined. #ifdef TG_NOATOMICS typedef int rc_t; static void rc_init(rc_t *rc) { *rc = 0; } static void rc_retain(rc_t *rc) { *rc++; } static bool rc_release(rc_t *rc) { *rc--; return *rc == 1; } #else #include /* The relaxed/release/acquire pattern is based on: http://boost.org/doc/libs/1_87_0/libs/atomic/doc/html/atomic/usage_examples.html */ typedef atomic_int rc_t; static void rc_init(rc_t *rc) { atomic_init(rc, 0); } static void rc_retain(rc_t *rc) { atomic_fetch_add_explicit(rc, 1, __ATOMIC_RELAXED); } static bool rc_release(rc_t *rc) { if (atomic_fetch_sub_explicit(rc, 1, __ATOMIC_RELEASE) == 1) { atomic_thread_fence(__ATOMIC_ACQUIRE); return true; } return false; } #endif struct head { rc_t rc; // 4-byte atomic reference counter uint8_t noheap; // bool uint8_t base; // enum base uint8_t type; // enum tg_geom_type uint8_t flags; // enum flags }; // The obj param in the following head functions must be: // head, tg_geom, tg_ring, tg_line, or boxed_point. static void setnoheap(void *obj, bool noheap) { struct head *head = (struct head*)obj; head->noheap = noheap; } static bool getnoheap(const void *obj) { const struct head *head = (struct head*)obj; return head->noheap; } static void setbase(void *obj, enum base base) { struct head *head = (struct head*)obj; head->base = base; } static enum base getbase(const void *obj) { const struct head *head = (struct head*)obj; return head->base; } static void settype(void *obj, enum tg_geom_type type) { struct head *head = (struct head*)obj; head->type = type; } static enum tg_geom_type gettype(const void *obj) { const struct head *head = (struct head*)obj; return head->type; } static void setflags(void *obj, enum flags flags) { struct head *head = (struct head*)obj; head->flags |= flags; } static enum flags getflags(const void *obj) { const struct head *head = (struct head*)obj; return head->flags; } /// A ring is series of tg_segment which creates a shape that does not /// self-intersect and is fully closed, where the start and end points are the /// exact same value. /// /// **Creating** /// /// To create a new ring use the tg_ring_new() function. /// /// ``` /// struct tg_ring *ring = tg_ring_new(points, npoints); /// ``` /// /// **Upcasting** /// /// A tg_ring can always be safely upcasted to a tg_poly or tg_geom; allowing /// it to use any tg_poly_*() or tg_geom_*() function. /// /// ``` /// struct tg_poly *poly = (struct tg_poly*)ring; // Cast to a tg_poly /// struct tg_geom *geom = (struct tg_geom*)ring; // Cast to a tg_geom /// ``` /// @see RingFuncs /// @see PolyFuncs struct tg_ring { struct head head; bool closed; bool clockwise; bool convex; double area; int npoints; int nsegs; struct tg_rect rect; struct index *index; struct ystripes *ystripes; struct tg_point points[]; }; /// A line is a series of tg_segment that make up a linestring. /// /// **Creating** /// /// To create a new line use the tg_line_new() function. /// /// ``` /// struct tg_line *line = tg_line_new(points, npoints); /// ``` /// /// **Upcasting** /// /// A tg_line can always be safely upcasted to a tg_geom; allowing /// it to use any tg_geom_*() function. /// /// ``` /// struct tg_geom *geom = (struct tg_geom*)line; // Cast to a tg_geom /// ``` /// /// @see LineFuncs struct tg_line { int _; }; /// A polygon consists of one exterior ring and zero or more holes. /// /// **Creating** /// /// To create a new polygon use the tg_poly_new() function. /// /// ``` /// struct tg_poly *poly = tg_poly_new(exterior, holes, nholes); /// ``` /// /// **Upcasting** /// /// A tg_poly can always be safely upcasted to a tg_geom; allowing /// it to use any tg_geom_*() function. /// /// ``` /// struct tg_geom *geom = (struct tg_geom*)poly; // Cast to a tg_geom /// ``` /// /// @see PolyFuncs struct tg_poly { struct head head; struct tg_ring *exterior; int nholes; struct tg_ring **holes; }; struct multi { struct tg_geom **geoms; int ngeoms; struct tg_rect rect; // unioned rect child geometries struct index *index; // geometry index, or NULL if not indexed int *ixgeoms; // indexed geometries, or NULL if not indexed }; /// A geometry is the common generic type that can represent a Point, /// LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, or /// GeometryCollection. /// /// For geometries that are derived from GeoJSON, they may have additional /// attributes such as being a Feature or a FeatureCollection; or include /// extra json fields. /// /// **Creating** /// /// To create a new geometry use one of the @ref GeometryConstructors or /// @ref GeometryParsing functions. /// /// ``` /// struct tg_geom *geom = tg_geom_new_point(point); /// struct tg_geom *geom = tg_geom_new_polygon(poly); /// struct tg_geom *geom = tg_parse_geojson(geojson); /// ``` /// /// **Upcasting** /// /// Other types, specifically tg_line, tg_ring, and tg_poly, can be safely /// upcasted to a tg_geom; allowing them to use any tg_geom_*() /// function. /// /// ``` /// struct tg_geom *geom1 = (struct tg_geom*)line; // Cast to a LineString /// struct tg_geom *geom2 = (struct tg_geom*)ring; // Cast to a Polygon /// struct tg_geom *geom3 = (struct tg_geom*)poly; // Cast to a Polygon /// ``` /// /// @see GeometryConstructors /// @see GeometryAccessors /// @see GeometryPredicates /// @see GeometryParsing /// @see GeometryWriting struct tg_geom { struct head head; union { struct tg_point point; struct tg_line *line; struct tg_poly *poly; struct multi *multi; }; union { struct { // TG_POINT double z; double m; }; struct { // !TG_POINT double *coords; // extra dimensional coordinates int ncoords; }; }; union { char *xjson; // extra json fields, such as "id", "properties", etc. char *error; // an error message, when flag IS_ERROR }; }; struct boxed_point { struct head head; struct tg_point point; }; #define todo(msg) { \ fprintf(stderr, "todo: %s, line: %d\n", (msg), __LINE__); \ exit(1); \ } static_assert(sizeof(int) == 4 || sizeof(int) == 8, "invalid int size"); // Function attribute for noinline. #if defined(__GNUC__) #define __attr_noinline __attribute__((noinline)) #elif defined(_MSC_VER) #define __attr_noinline __declspec(noinline) #else #define __attr_noinline #endif // Fast floating-point min and max for gcc and clang on arm64 and x64. #if defined(__GNUC__) && defined(__aarch64__) // arm64 already uses branchless operations for fmin and fmax static inline double fmin0(double x, double y) { return fmin(x, y); } static inline double fmax0(double x, double y) { return fmax(x, y); } static inline float fminf0(float x, float y) { return fminf(x, y); } static inline float fmaxf0(float x, float y) { return fmaxf(x, y); } #elif defined(__GNUC__) && defined(__x86_64__) // gcc/amd64 sometimes uses branching with fmin/fmax. // This code use single a asm op instead. // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=94497 #pragma GCC diagnostic push #ifdef __clang__ #pragma clang diagnostic ignored "-Wlanguage-extension-token" #endif static inline double fmin0(double x, double y) { asm("minsd %1, %0" : "+x" (x) : "x" (y)); return x; } static inline double fmax0(double x, double y) { asm("maxsd %1, %0" : "+x" (x) : "x" (y)); return x; } static inline float fminf0(float x, float y) { asm("minss %1, %0" : "+x" (x) : "x" (y)); return x; } static inline float fmaxf0(float x, float y) { asm("maxss %1, %0" : "+x" (x) : "x" (y)); return x; } #pragma GCC diagnostic pop #else // for everything else, let the compiler figure it out. static inline double fmin0(double x, double y) { return x < y ? x : y; } static inline double fmax0(double x, double y) { return x > y ? x : y; } static inline float fminf0(double x, float y) { return x < y ? x : y; } static inline float fmaxf0(float x, float y) { return x > y ? x : y; } #endif static inline double fclamp0(double f, double min, double max) { return fmin0(fmax0(f, min), max); } static bool feq(double x, double y) { return !((x < y) | (x > y)); } static bool eq_zero(double x) { return feq(x, 0); } static bool collinear( double x1, double y1, // point 1 double x2, double y2, // point 2 double x3, double y3 // point 3 ) { bool x1x2 = feq(x1, x2); bool x1x3 = feq(x1, x3); bool x2x3 = feq(x2, x3); bool y1y2 = feq(y1, y2); bool y1y3 = feq(y1, y3); bool y2y3 = feq(y2, y3); if (x1x2) { return x1x3; } if (y1y2) { return y1y3; } if ((x1x2 & y1y2) | (x1x3 & y1y3) | (x2x3 & y2y3)) { return true; } double cx1 = x3 - x1; double cy1 = y3 - y1; double cx2 = x2 - x1; double cy2 = y2 - y1; double s1 = cx1 * cy2; double s2 = cy1 * cx2; // Check if precision was lost. double s3 = (s1 / cy2) - cx1; double s4 = (s2 / cx2) - cy1; if (s3 < 0) { s1 = nexttoward(s1, -INFINITY); } else if (s3 > 0) { s1 = nexttoward(s1, +INFINITY); } if (s4 < 0) { s2 = nexttoward(s2, -INFINITY); } else if (s4 > 0) { s2 = nexttoward(s2, +INFINITY); } return eq_zero(s1 - s2); } static double length(double x1, double y1, double x2, double y2) { return sqrt((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2)); } #ifndef ludo #define ludo #define ludo1(i,f) f; i++; #define ludo2(i,f) ludo1(i,f); ludo1(i,f); #define ludo4(i,f) ludo2(i,f); ludo2(i,f); #define ludo8(i,f) ludo4(i,f); ludo4(i,f); #define ludo16(i,f) ludo8(i,f); ludo8(i,f); #define ludo32(i,f) ludo16(i,f); ludo16(i,f); #define ludo64(i,f) ludo32(i,f); ludo32(i,f); #define for1(i,n,f) while(i+1<=(n)) { ludo1(i,f); } #define for2(i,n,f) while(i+2<=(n)) { ludo2(i,f); } for1(i,n,f); #define for4(i,n,f) while(i+4<=(n)) { ludo4(i,f); } for1(i,n,f); #define for8(i,n,f) while(i+8<=(n)) { ludo8(i,f); } for1(i,n,f); #define for16(i,n,f) while(i+16<=(n)) { ludo16(i,f); } for1(i,n,f); #define for32(i,n,f) while(i+32<=(n)) { ludo32(i,f); } for1(i,n,f); #define for64(i,n,f) while(i+64<=(n)) { ludo64(i,f); } for1(i,n,f); #endif static size_t grow_cap(size_t cap, size_t init_cap) { return cap == 0 ? init_cap : cap < 1000 ? cap * 2 : cap * 1.25; } #define print_segment(s) { \ printf("(%f %f,%f %f)", (s).a.x, (s).a.y, (s).b.x, (s).b.y); \ } #define print_geom(g) { \ size_t sz = tg_geom_wkt(g, 0, 0); \ char *wkt = tg_malloc(sz+1); \ assert(wkt); \ tg_geom_wkt(g, wkt, sz+1); \ printf("%s\n", wkt); \ free(wkt); \ } ///////////////////// // global behaviors ///////////////////// // Global variables shared by all TG functions. static void *(*_malloc)(size_t) = NULL; static void *(*_realloc)(void*, size_t) = NULL; static void (*_free)(void*) = NULL; static enum tg_index default_index = TG_NATURAL; static int default_index_spread = 16; static bool print_fixed_floats = false; /// Set the floating point printing to be fixed size. /// By default floating points are printed using their smallest textual /// representation. Such as 800000 is converted to "8e5". This is ideal when /// both accuracy and size are desired. But there may be times when only /// fixed epresentations are wanted, in that case set this param to true. void tg_env_set_print_fixed_floats(bool print) { print_fixed_floats = print; } /// Allow for configuring a custom allocator. /// /// This overrides the built-in malloc, realloc, and free functions for all /// TG operations. /// @warning This function, if needed, should be called **only once** at /// program start up and prior to calling any other tg_*() function. /// @see GlobalFuncs void tg_env_set_allocator( void *(*malloc)(size_t), void *(*realloc)(void*, size_t), void (*free)(void*)) { _malloc = malloc; _realloc = realloc; _free = free; } void *tg_malloc(size_t nbytes) { void *ptr = (_malloc?_malloc:malloc)(nbytes); #ifdef TG_ABORTNOMEM if (!ptr) { fprintf(stderr, "Out of memory\n"); abort(); } #endif return ptr; } void *tg_realloc(void *ptr, size_t nbytes) { ptr = (_realloc?_realloc:realloc)(ptr, nbytes); #ifdef TG_ABORTNOMEM if (!ptr) { fprintf(stderr, "Out of memory\n"); abort(); } #endif return ptr; } void tg_free(void *ptr) { (_free?_free:free)(ptr); } /// Set the geometry indexing default. /// /// This is a global override to the indexing for all yet-to-be created /// geometries. /// @warning This function, if needed, should be called **only once** at /// program start up and prior to calling any other tg_*() function. /// @see [tg_index](.#tg_index) /// @see tg_env_set_index() /// @see GlobalFuncs void tg_env_set_index(enum tg_index ix) { switch (ix) { case TG_NONE: case TG_NATURAL: case TG_YSTRIPES: // only change for none, natural, and ystripes default_index = ix; break; default: // no change break; } } /// Get the current geometry indexing default. /// @see [tg_index](.#tg_index) /// @see tg_env_set_index() /// @see GlobalFuncs enum tg_index tg_env_get_default_index(void) { return default_index; } /// Set the default index spread. /// /// The "spread" is how many rectangles are grouped together on an indexed /// level before propagating up to a higher level. /// /// Default is 16. /// /// This is a global override to the indexing spread for all yet-to-be created /// geometries. /// @warning This function, if needed, should be called **only once** at /// program start up and prior to calling any other tg_*() function. /// @see [tg_index](.#tg_index) /// @see tg_env_set_index() /// @see About TG indexing /// @see GlobalFuncs void tg_env_set_index_spread(int spread) { // only allow spreads between 2 and 1024 if (spread >= 2 && spread <= 4096) { default_index_spread = spread; } } int tg_env_get_index_spread(void) { return default_index_spread; } enum tg_index tg_index_with_spread(enum tg_index ix, int spread) { // Only 16 bits of the tg_index is used. // first 4 bits is the index. The next 12 is the spread. if (spread != 0) { spread = spread < 2 ? 2 : spread > 4096 ? 4096 : spread; spread--; // ensure range 1-4095 (but will actually be 2-4096) } return (ix & 0xF) | (spread << 4); } enum tg_index tg_index_extract_spread(enum tg_index ix, int *spread) { int ixspread = ((unsigned int)(ix >> 4)) & 4095; if (ixspread > 0) { ixspread++; } if (ixspread == 0) { ixspread = tg_env_get_index_spread(); } if (spread) { *spread = ixspread; } return ix & 0xF; } //////////////////// // point //////////////////// static bool pteq(struct tg_point a, struct tg_point b) { return feq(a.x, b.x) && feq(a.y, b.y); } /// Returns the minimum bounding rectangle of a point. /// @see PointFuncs struct tg_rect tg_point_rect(struct tg_point point) { return (struct tg_rect){ .min = point, .max = point }; } /// Tests whether a point fully contains another point. /// @see PointFuncs bool tg_point_covers_point(struct tg_point point, struct tg_point other) { return pteq(point, other); } bool tg_point_contains_point(struct tg_point point, struct tg_point other) { return pteq(point, other); } /// Tests whether a point intersects another point. /// @see PointFuncs bool tg_point_intersects_point(struct tg_point a, struct tg_point b) { return pteq(a, b); } bool tg_point_touches_point(struct tg_point a, struct tg_point b) { // Points do not have boundaries (void)a; (void)b; return false; } /// Tests whether a point fully contains a rectangle. /// @see PointFuncs bool tg_point_covers_rect(struct tg_point point, struct tg_rect rect) { return pteq(rect.min, point) && pteq(rect.max, point); } /// Tests whether a point fully intersects a rectangle. /// @see PointFuncs bool tg_point_intersects_rect(struct tg_point point, struct tg_rect rect) { return tg_rect_covers_point(rect, point); } /// Tests whether a point fully contains a line. /// @see PointFuncs bool tg_point_covers_line(struct tg_point point, const struct tg_line *line) { return !tg_line_empty(line) && tg_point_covers_rect(point, tg_line_rect(line)); } bool tg_point_contains_line(struct tg_point point, const struct tg_line *line) { return !tg_line_empty(line) && tg_point_covers_rect(point, tg_line_rect(line)); } /// Tests whether a point intersects a line. /// @see PointFuncs bool tg_point_intersects_line(struct tg_point point, const struct tg_line *line) { return tg_line_intersects_point(line, point); } bool tg_point_touches_line(struct tg_point point, const struct tg_line *line) { int nsegs = tg_line_num_segments(line); if (nsegs == 0) { return false; } struct tg_segment s0 = tg_line_segment_at(line, 0); struct tg_segment sN = tg_line_segment_at(line, nsegs-1); return pteq(point, s0.a) || pteq(point, sN.b); } /// Tests whether a point fully contains a polygon. /// @see PointFuncs bool tg_point_covers_poly(struct tg_point point, const struct tg_poly *poly) { return !tg_poly_empty(poly) && tg_point_covers_rect(point, tg_poly_rect(poly)); } bool tg_point_contains_poly(struct tg_point point, const struct tg_poly *poly) { // not possible (void)point; (void)poly; return false; } /// Tests whether a point intersects a polygon. /// @see PointFuncs bool tg_point_intersects_poly(struct tg_point point, const struct tg_poly *poly) { return tg_poly_intersects_point(poly, point); } bool tg_point_touches_poly(struct tg_point point, const struct tg_poly *poly) { // Return true if the point touches the boundary of the exterior ring or // the boundary of the interior holes. const struct tg_ring *ring = tg_poly_exterior(poly); if (tg_line_covers_point((struct tg_line*)ring, point)) { return true; } int nholes = tg_poly_num_holes(poly); for (int i = 0; i < nholes; i++) { const struct tg_ring *ring = tg_poly_hole_at(poly, i); if (tg_line_covers_point((struct tg_line*)ring, point)) { return true; } } return false; } //////////////////// // segment //////////////////// static bool point_on_segment(struct tg_point p, struct tg_segment s) { if (!tg_rect_covers_point(tg_segment_rect(s), p)) { return false; } return collinear(s.a.x, s.a.y, s.b.x, s.b.y, p.x, p.y); } static enum tg_raycast_result raycast(struct tg_segment seg, struct tg_point p) { struct tg_rect r = tg_segment_rect(seg); if (p.y < r.min.y || p.y > r.max.y) { return TG_OUT; } if (p.x < r.min.x) { if (p.y != r.min.y && p.y != r.max.y) { return TG_IN; } } else if (p.x > r.max.x) { if (r.min.y != r.max.y && r.min.x != r.max.x) { return TG_OUT; } } struct tg_point a = seg.a; struct tg_point b = seg.b; if (b.y < a.y) { struct tg_point t = a; a = b; b = t; } if (pteq(p, a) || pteq(p, b)) { return TG_ON; } if (a.y == b.y) { if (a.x == b.x) { return TG_OUT; } if (p.y == b.y) { if (!(p.x < r.min.x || p.x > r.max.x)) { return TG_ON; } } } if (a.x == b.x && p.x == b.x) { if (p.y >= a.y && p.y <= b.y) { return TG_ON; } } if (collinear(a.x, a.y, b.x, b.y, p.x, p.y)) { if (p.x < r.min.x) { if (r.min.y == r.max.y) { return TG_OUT; } } else if (p.x > r.max.x) { return TG_OUT; } return TG_ON; } if (p.y == a.y || p.y == b.y) { p.y = nexttoward(p.y, INFINITY); } if (p.y < a.y || p.y > b.y) { return TG_OUT; } if (a.x > b.x) { if (p.x >= a.x) { return TG_OUT; } if (p.x <= b.x) { return TG_IN; } } else { if (p.x >= b.x) { return TG_OUT; } if (p.x <= a.x) { return TG_IN; } } if ((p.y-a.y)/(p.x-a.x) >= (b.y-a.y)/(b.x-a.x)) { return TG_IN; } return TG_OUT; } /// Performs the raycast operation of a point on segment enum tg_raycast_result tg_raycast(struct tg_segment seg, struct tg_point p) { return raycast(seg, p); } struct tg_point tg_point_move(struct tg_point point, double delta_x, double delta_y) { return (struct tg_point){ .x = point.x + delta_x, .y = point.y + delta_y }; } struct tg_segment tg_segment_move(struct tg_segment seg, double delta_x, double delta_y) { return (struct tg_segment){ .a = tg_point_move(seg.a, delta_x, delta_y), .b = tg_point_move(seg.b, delta_x, delta_y), }; } /// Tests whether a segment fully contains a point. /// @see SegmentFuncs bool tg_segment_covers_point(struct tg_segment seg, struct tg_point p) { return point_on_segment(p, seg); } /// Tests whether a segment fully contains another segment. /// @see SegmentFuncs bool tg_segment_covers_segment(struct tg_segment a, struct tg_segment b) { return tg_segment_covers_point(a, b.a) && tg_segment_covers_point(a, b.b); } static void segment_fill_rect(const struct tg_segment *seg, struct tg_rect *rect) { rect->min.x = fmin0(seg->a.x, seg->b.x); rect->min.y = fmin0(seg->a.y, seg->b.y); rect->max.x = fmax0(seg->a.x, seg->b.x); rect->max.y = fmax0(seg->a.y, seg->b.y); } /// Returns the minimum bounding rectangle of a segment. /// @see SegmentFuncs struct tg_rect tg_segment_rect(struct tg_segment seg) { struct tg_rect rect; segment_fill_rect(&seg, &rect); return rect; } static bool rect_intersects_rect(struct tg_rect *a, struct tg_rect *b) { return !(b->min.x > a->max.x || b->max.x < a->min.x || b->min.y > a->max.y || b->max.y < a->min.y); } /// Tests whether a rectangle intersects another rectangle. /// @see RectFuncs bool tg_rect_intersects_rect(struct tg_rect a, struct tg_rect b) { return rect_intersects_rect(&a, &b); } /// Tests whether a rectangle full contains another rectangle. bool tg_rect_covers_rect(struct tg_rect a, struct tg_rect b) { return !(b.min.x < a.min.x || b.max.x > a.max.x || b.min.y < a.min.y || b.max.y > a.max.y); } /// Returns the number of points. Always 5 for rects. int tg_rect_num_points(struct tg_rect r) { (void)r; return 5; } /// Returns the number of segments. Always 4 for rects. int tg_rect_num_segments(struct tg_rect r) { (void)r; return 4; } /// Returns the point at index. struct tg_point tg_rect_point_at(struct tg_rect r, int index) { switch (index) { case 0: return (struct tg_point){ r.min.x, r.min.y }; case 1: return (struct tg_point){ r.max.x, r.min.y }; case 2: return (struct tg_point){ r.max.x, r.max.y }; case 3: return (struct tg_point){ r.min.x, r.max.y }; case 4: return (struct tg_point){ r.min.x, r.min.y }; default: return (struct tg_point){ 0 }; } } /// Returns the segment at index. struct tg_segment tg_rect_segment_at(struct tg_rect r, int index) { switch (index) { case 0: return (struct tg_segment){ { r.min.x, r.min.y}, { r.max.x, r.min.y} }; case 1: return (struct tg_segment){ { r.max.x, r.min.y}, { r.max.x, r.max.y} }; case 2: return (struct tg_segment){ { r.max.x, r.max.y}, { r.min.x, r.max.y} }; case 3: return (struct tg_segment){ { r.min.x, r.max.y}, { r.min.x, r.min.y} }; default: return (struct tg_segment){ 0 }; } } /// Tests whether a segment intersects another segment. /// @see SegmentFuncs bool tg_segment_intersects_segment(struct tg_segment seg_a, struct tg_segment seg_b) { struct tg_point a = seg_a.a; struct tg_point b = seg_a.b; struct tg_point c = seg_b.a; struct tg_point d = seg_b.b; if (!tg_rect_intersects_rect(tg_segment_rect(seg_a), tg_segment_rect(seg_b))) { return false; } if (pteq(seg_a.a, seg_b.a) || pteq(seg_a.a, seg_b.b) || pteq(seg_a.b, seg_b.a) || pteq(seg_a.b, seg_b.b)) { return true; } double cmpx = c.x-a.x; double cmpy = c.y-a.y; double rx = b.x-a.x; double ry = b.y-a.y; double cmpxr = cmpx*ry - cmpy*rx; if (eq_zero(cmpxr)) { // collinear, and so intersect if they have any overlap if (!(((c.x-a.x <= 0) != (c.x-b.x <= 0))) || ((c.y-a.y <= 0) != (c.y-b.y <= 0))) { return tg_segment_covers_point(seg_a, seg_b.a) || tg_segment_covers_point(seg_a, seg_b.b) || tg_segment_covers_point(seg_b, seg_a.a) || tg_segment_covers_point(seg_b, seg_a.b); } return true; } double sx = d.x-c.x; double sy = d.y-c.y; double rxs = rx*sy - ry*sx; if (eq_zero(rxs)) { // Segments are parallel. return false; } double cmpxs = cmpx*sy - cmpy*sx; double rxsr = 1 / rxs; double t = cmpxs * rxsr; double u = cmpxr * rxsr; if (!((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1))) { return false; } return true; } /// Tests whether a segment fully contains a rectangle. /// @see SegmentFuncs bool tg_segment_covers_rect(struct tg_segment seg, struct tg_rect rect) { return tg_segment_covers_point(seg, rect.min) && tg_segment_covers_point(seg, rect.max); } ////////////////// // ystripes ////////////////// struct ystripe { int count; int *indexes; }; struct tg_buf { uint8_t *data; size_t len, cap; }; static bool tg_buf_ensure(struct tg_buf *buf, size_t len) { if (buf->cap-buf->len >= len) return true; size_t cap = buf->cap; do { cap = grow_cap(cap, 16); } while (cap-buf->len < len); uint8_t *data = tg_realloc(buf->data, cap+1); if (!data) return false; buf->data = data; buf->cap = cap; buf->data[buf->cap] = '\0'; return true; } // tg_buf_append_byte append byte to buffer. // The buf->len should be greater than before, otherwise out of memory. static bool tg_buf_append_byte(struct tg_buf *buf, uint8_t b) { if (!tg_buf_ensure(buf, 1)) return false; buf->data[buf->len++] = b; return true; } static bool tg_buf_append_bytes(struct tg_buf *buf, uint8_t *bytes, size_t nbytes) { if (!tg_buf_ensure(buf, nbytes)) return false; memcpy(buf->data+buf->len, bytes, nbytes); buf->len += nbytes; return true; } static bool tg_buf_trunc(struct tg_buf *buf) { if (buf->cap-buf->len > 8) { uint8_t *data = tg_realloc(buf->data, buf->len); if (!data) return false; buf->data = data; buf->cap = buf->len; } return true; } ////////////////// // index ////////////////// #if !defined(TG_IXFLOAT64) // 32-bit floats struct ixpoint { float x; float y; }; struct ixrect { struct ixpoint min; struct ixpoint max; }; static inline void ixrect_expand(struct ixrect *rect, struct ixrect *other) { rect->min.x = fminf0(rect->min.x, other->min.x); rect->min.y = fminf0(rect->min.y, other->min.y); rect->max.x = fmaxf0(rect->max.x, other->max.x); rect->max.y = fmaxf0(rect->max.y, other->max.y); } // fdown/fup returns a value that is a little larger or smaller. // Works similarly to nextafter, though it's faster and less accurate. // fdown(d) -> nextafter(d, -INFINITY); // fup(d) -> nextafter(d, INFINITY); // fnext0 returns the next value after d. // Param dir can be 0 (towards -INFINITY) or 1 (towards INFINITY). static double fnext0(double d, int dir) { static double vals[2] = { 1.0 - 1.0/8388608.0, /* towards zero */ 1.0 + 1.0/8388608.0 /* away from zero */ }; return d * vals[((d<0)+dir)&1]; } static double fdown(double d) { return fnext0(d, 0); } static double fup(double d) { return fnext0(d, 1); } static void tg_rect_to_ixrect(struct tg_rect *rect, struct ixrect *ixrect) { ixrect->min.x = fdown(rect->min.x); ixrect->min.y = fdown(rect->min.y); ixrect->max.x = fup(rect->max.x); ixrect->max.y = fup(rect->max.y); } #else // 64-bit floats struct ixpoint { double x; double y; }; struct ixrect { struct ixpoint min; struct ixpoint max; }; static inline void ixrect_expand(struct ixrect *rect, struct ixrect *other) { rect->min.x = fmin0(rect->min.x, other->min.x); rect->min.y = fmin0(rect->min.y, other->min.y); rect->max.x = fmax0(rect->max.x, other->max.x); rect->max.y = fmax0(rect->max.y, other->max.y); } static inline void tg_rect_to_ixrect(struct tg_rect *rect, struct ixrect *ixrect) { ixrect->min.x = rect->min.x; ixrect->min.y = rect->min.y; ixrect->max.x = rect->max.x; ixrect->max.y = rect->max.y; } #endif struct level { int nrects; struct ixrect *rects; }; struct index { size_t memsz; // memory size of index int spread; // index spread (num items in each "node") int nlevels; // number of levels struct level levels[]; // all levels starting with root }; static inline void tg_point_to_ixpoint(struct tg_point *point, struct ixpoint *ixpoint) { ixpoint->x = point->x; ixpoint->y = point->y; } static inline void ixrect_to_tg_rect(struct ixrect *ixrect, struct tg_rect *rect) { rect->min.x = ixrect->min.x; rect->min.y = ixrect->min.y; rect->max.x = ixrect->max.x; rect->max.y = ixrect->max.y; } static inline bool ixrect_intersects_ixrect(struct ixrect *a, struct ixrect *b) { if (a->min.y > b->max.y || a->max.y < b->min.y) return false; if (a->min.x > b->max.x || a->max.x < b->min.x) return false; return true; } static int index_spread(const struct index *index) { return index ? index->spread : 0; } static int index_num_levels(const struct index *index) { return index ? index->nlevels : 0; } static int index_level_num_rects(const struct index *index, int levelidx) { if (!index) return 0; if (levelidx < 0 || levelidx >= index->nlevels) return 0; return index->levels[levelidx].nrects; } static struct tg_rect index_level_rect(const struct index *index, int levelidx, int rectidx) { if (!index) return (struct tg_rect) { 0 }; if (levelidx < 0 || levelidx >= index->nlevels) { return (struct tg_rect) { 0 }; } const struct level *level = &index->levels[levelidx]; if (rectidx < 0 || rectidx >= level->nrects) { return (struct tg_rect) { 0 }; } struct tg_rect rect; ixrect_to_tg_rect(&level->rects[rectidx], &rect); return rect; } static int calc_num_keys(int spread, int level, int count) { return (int)ceil((double)count / pow((double)spread, (double)level)); } static int calc_num_levels(int spread, int count) { int level = 1; for (; calc_num_keys(spread, level, count) > 1; level++); return level; } static size_t aligned_size(size_t size) { return size&7 ? size+8-(size&7) : size; } size_t tg_aligned_size(size_t size) { return aligned_size(size); } // calc_index_size returns the space needed to hold all the data for an index. static size_t calc_index_size(int ixspread, int nsegs, int *nlevelsout) { int nlevels = calc_num_levels(ixspread, nsegs); int inlevels = nlevels-1; size_t size = sizeof(struct index); size += inlevels*sizeof(struct level); for (int i = 0; i < inlevels; i++) { int nkeys = calc_num_keys(ixspread, inlevels-i, nsegs); size += nkeys*sizeof(struct ixrect); } *nlevelsout = nlevels; size = aligned_size(size); return size; } struct ystripes { size_t memsz; int nstripes; struct ystripe stripes[]; }; static bool process_ystripes(struct tg_ring *ring) { double score = tg_ring_polsby_popper_score(ring); int nstripes = ring->nsegs * score; nstripes = fmax0(nstripes, 32); double height = ring->rect.max.y - ring->rect.min.y; // ycounts is used to log the number of segments in each stripe. int *ycounts = tg_malloc(nstripes*sizeof(int)); if (!ycounts) { return false; } memset(ycounts, 0, nstripes*sizeof(int)); // nmap is the the total number of mapped segments. This will be greater // than the total number of segments because a segment can exist in // multiple stripes. int nmap = 0; // Run through each segment and determine which stripes it belongs to and // increment the nmap counter. for (int i = 0; i < ring->nsegs; i++) { double ymin = fmin0(ring->points[i].y, ring->points[i+1].y); double ymax = fmax0(ring->points[i].y, ring->points[i+1].y); int min = (ymin - ring->rect.min.y) / height * (double)nstripes; int max = (ymax - ring->rect.min.y) / height * (double)nstripes; max = fmin0(max, nstripes-1); min = fmin0(fmax0(min, 0), max); for (int j = min; j <= max; j++) { ycounts[j]++; nmap++; } } size_t tsize = sizeof(struct ystripes); tsize += nstripes*sizeof(struct ystripe); size_t mark = tsize; tsize += nmap*sizeof(int); struct ystripes *ystripes = tg_malloc(tsize); if (!ystripes) { tg_free(ycounts); return false; } ystripes->memsz = tsize; ystripes->nstripes = nstripes; size_t pos = mark; for (int i = 0; i < nstripes; i++) { ystripes->stripes[i].count = 0; ystripes->stripes[i].indexes = (void*)&((char*)ystripes)[pos]; pos += ycounts[i]*sizeof(int); } tg_free(ycounts); for (int i = 0; i < ring->nsegs; i++) { double ymin = fmin0(ring->points[i].y, ring->points[i+1].y); double ymax = fmax0(ring->points[i].y, ring->points[i+1].y); int min = (ymin - ring->rect.min.y) / height * (double)nstripes; int max = (ymax - ring->rect.min.y) / height * (double)nstripes; max = fmin0(max, nstripes-1); min = fmin0(fmax0(min, 0), max); for (int j = min; j <= max; j++) { struct ystripe *stripe = &ystripes->stripes[j]; stripe->indexes[stripe->count++] = i; } } ring->ystripes = ystripes; return true; } static struct tg_segment ring_segment_at(const struct tg_ring *ring, int index); ////////////////// // rect ////////////////// /// Tests whether a rectangle fully contains a point using xy coordinates. /// @see RectFuncs bool tg_rect_covers_xy(struct tg_rect rect, double x, double y) { return !(x < rect.min.x || y < rect.min.y || x > rect.max.x || y > rect.max.y); } /// Tests whether a rectangle fully contains a point. /// @see RectFuncs bool tg_rect_covers_point(struct tg_rect rect, struct tg_point point) { return tg_rect_covers_xy(rect, point.x, point.y); } /// Tests whether a rectangle and a point intersect. /// @see RectFuncs bool tg_rect_intersects_point(struct tg_rect rect, struct tg_point point) { return tg_rect_covers_point(rect, point); } /// Returns the center point of a rectangle /// @see RectFuncs struct tg_point tg_rect_center(struct tg_rect rect) { return (struct tg_point){ .x = (rect.max.x + rect.min.x) / 2, .y = (rect.max.y + rect.min.y) / 2, }; } static void rect_inflate(struct tg_rect *rect, struct tg_rect *other) { rect->min.x = fmin0(rect->min.x, other->min.x); rect->min.y = fmin0(rect->min.y, other->min.y); rect->max.x = fmax0(rect->max.x, other->max.x); rect->max.y = fmax0(rect->max.y, other->max.y); } static void rect_inflate_point(struct tg_rect *rect, struct tg_point *point) { rect->min.x = fmin0(rect->min.x, point->x); rect->min.y = fmin0(rect->min.y, point->y); rect->max.x = fmax0(rect->max.x, point->x); rect->max.y = fmax0(rect->max.y, point->y); } /// Expands a rectangle to include an additional rectangle. /// @param rect Input rectangle /// @param other Input rectangle /// @return Expanded rectangle /// @see RectFuncs struct tg_rect tg_rect_expand(struct tg_rect rect, struct tg_rect other) { rect_inflate(&rect, &other); return rect; } /// Expands a rectangle to include an additional point. /// @param rect Input rectangle /// @param point Input Point /// @return Expanded rectangle /// @see RectFuncs struct tg_rect tg_rect_expand_point(struct tg_rect rect, struct tg_point point) { rect_inflate_point(&rect, &point); return rect; } // rect_to_ring fills the ring with rect data. static void rect_to_ring(struct tg_rect rect, struct tg_ring *ring) { memset(ring, 0, sizeof(struct tg_ring)); setbase(ring, BASE_RING); settype(ring, TG_POLYGON); ring->rect = rect; ring->closed = true; ring->convex = true; ring->npoints = 5; ring->nsegs = 4; for (int i = 0; i < 5; i++) { ring->points[i] = tg_rect_point_at(rect, i); } } static void segment_to_ring(struct tg_segment seg, struct tg_ring *ring) { memset(ring, 0, sizeof(struct tg_ring)); ring->rect = tg_segment_rect(seg); ring->closed = false; ring->convex = true; ring->npoints = 2; ring->nsegs = 1; ring->points[0] = seg.a; ring->points[1] = seg.b; } void tg_rect_search(const struct tg_rect rect, struct tg_rect target, bool(*iter)(struct tg_segment seg, int index, void *udata), void *udata) { int nsegs = tg_rect_num_segments(rect); for (int i = 0; i < nsegs; i++) { struct tg_segment seg = tg_rect_segment_at(rect, i); if (tg_rect_intersects_rect(tg_segment_rect(seg), target)) { if (!iter(seg, i, udata)) return; } } } /// Tests whether a rectangle fully contains line. bool tg_rect_covers_line(struct tg_rect rect, const struct tg_line *line) { return !tg_line_empty(line) && tg_rect_covers_rect(rect, tg_line_rect(line)); } //////////////////// // ring //////////////////// static void fill_in_upper_index_levels(struct index *index) { int ixspread = index->spread; for (int lvl = 1; lvl < index->nlevels; lvl++) { struct level *level = &index->levels[index->nlevels-1-lvl+0]; struct level *plevel = &index->levels[index->nlevels-1-lvl+1]; for (int i = 0; i < level->nrects; i++) { int s = i*ixspread; int e = s+ixspread; if (e > plevel->nrects) e = plevel->nrects; struct ixrect rect = plevel->rects[s]; for (int j = s+1; j < e; j++) { ixrect_expand(&rect, &plevel->rects[j]); } level->rects[i] = rect; } } } // stack_ring returns a ring on the stack that has enough points to be capable // of storing a rectangle. This is here to allow for tg_ring style operations // on tg_rect without an allocation or vla. #define stack_ring() \ (struct tg_ring*)(&(char[sizeof(struct tg_ring)+sizeof(struct tg_point)*5]){0}) static struct tg_rect process_points(const struct tg_point *points, int npoints, struct tg_point *ring_points, struct index *index, bool *convex, bool *clockwise, double *area) { struct tg_rect rect = { 0 }; if (npoints < 2) { if (points) { memcpy(ring_points, points, sizeof(struct tg_point) * npoints); } *convex = false; *clockwise = false; *area = 0; return rect; } struct tg_point a, b, c; bool concave = false; int dir = 0; double cwc = 0; int ixspread = index ? index->spread : 0; // Fill the initial rectangle with the first point. rect.min = points[0]; rect.max = points[0]; // struct tg_rect spreadrect; spreadrect.min = points[0]; spreadrect.max = points[0]; // gather some point positions for concave and clockwise detection #define step_gather_abc_nowrap() { \ a = points[i]; \ b = points[i+1]; \ c = points[i+2]; \ ring_points[i] = a; \ } #define step_gather_abc_wrap() { \ a = points[i]; \ if (i < npoints-2) { \ b = points[i+1]; \ c = points[i+2]; \ } else if (i == npoints-1) { \ b = points[0]; \ c = points[1]; \ } else { \ b = points[i+1]; \ c = points[0]; \ } \ ring_points[i] = a; \ } #define step_inflate_rect_no_index() { \ rect_inflate_point(&rect, &a); \ } #define inflate_mbr_and_copy_next_index_rect() { \ rect_inflate(&rect, &spreadrect); \ /* convert the index rect to tg_rect for storing in tmesa level */ \ tg_rect_to_ixrect(&spreadrect, \ &index->levels[index->nlevels-1].rects[r]); \ r++; \ } // process the rectangle inflation #define step_inflate_rect_with_index() { \ rect_inflate_point(&spreadrect, &a); \ j++; \ if (j == ixspread) { \ rect_inflate_point(&spreadrect, &b); \ inflate_mbr_and_copy_next_index_rect(); \ spreadrect.min = b; \ spreadrect.max = b; \ j = 0; \ } \ } #define step_calc_clockwise() { \ cwc += (b.x - a.x) * (b.y + a.y); \ } #define step_calc_concave(label) { \ double z_cross_product = (b.x-a.x)*(c.y-b.y) - (b.y-a.y)*(c.x-b.x); \ if (dir == 0) { \ dir = z_cross_product < 0 ? -1 : 1; \ } else if (z_cross_product < 0) { \ if (dir == 1) { \ concave = true; \ i++; \ goto label; \ } \ } else if (z_cross_product > 0) { \ if (dir == -1) { \ concave = true; \ i++; \ goto label; \ } \ } \ } // The following code is a large loop that scans over every point and // performs various processing. // // There are two major branch groups, Indexed and Non-indexed. // - Indexed is when there is a provided tablemesa type index structure, // and each segment spread (usually eight segments at a time) are // naturally inflated into a single rectangle that is stored contiguously // in a series of rectangles. // - Non-indexed is when there is no provided index. The entire MBR is // inflated to included every point. // // For each group, there are the two outer loops. Non-wrapped and Wrapped. // - Non-wrapped takes the next points A, B, C from points[i+0], // points[i+1], and points[i+2], without checking the points array // boundary. This is safe because of the "npoints-3" condition. // This is be quick because all internal operations are loop-unrolled. // - Wrapped is the remaining loop steps, up to three. // // Finally, there is a Convex and Concave section. // - Convex is the initial state. Each step will do some calculations until // it is determined if the ring is concave. Once it's known to be concave // the code jumps to the Concave section. // - Concave section is just like the Convex section, but there is no // calculation needed because it's known that the ring is concave. int i = 0; int j = 0; int r = 0; // LOOP START // Convex section if (!index) { // Non-index branch group // Non-wrapped outer loop for (;i < npoints-3; i++) { step_gather_abc_nowrap(); step_inflate_rect_no_index(); step_calc_clockwise(); step_calc_concave(lconcave0); } lconcave0: // Wrapped outer loop for (;i < npoints; i++) { step_gather_abc_wrap(); step_inflate_rect_no_index(); step_calc_clockwise(); step_calc_concave(lconcave); } } else { // Index branch group // Non-wrapped outer loop for (;i < npoints-3; i++) { step_gather_abc_nowrap(); step_inflate_rect_with_index(); step_calc_clockwise(); step_calc_concave(lconcave1); } lconcave1: // Wrapped outer loop for (;i < npoints; i++) { step_gather_abc_wrap(); step_inflate_rect_with_index(); step_calc_clockwise(); step_calc_concave(lconcave); } } lconcave: // Concave section if (!index) { // Non-index branch group // Non-wrapped outer loop for (;i < npoints-3; i++) { step_gather_abc_nowrap(); step_inflate_rect_no_index(); step_calc_clockwise(); } // Wrapped outer loop for (;i < npoints; i++) { step_gather_abc_wrap(); step_inflate_rect_no_index(); step_calc_clockwise(); } } else { // Index branch group // Non-wrapped outer loop for (;i < npoints-3; i++) { step_gather_abc_nowrap(); step_inflate_rect_with_index(); step_calc_clockwise(); } // Wrapped outer loop for (;i < npoints; i++) { step_gather_abc_wrap(); step_inflate_rect_with_index(); step_calc_clockwise(); } } // LOOP END if (index) { // Fill in the final indexing rectangles if (r != index->levels[index->nlevels-1].nrects) { // There's one last index rectangle remaining. inflate_mbr_and_copy_next_index_rect(); } fill_in_upper_index_levels(index); } *area = fabs(cwc / 2.0); *convex = !concave; *clockwise = cwc > 0; return rect; } static int num_segments(const struct tg_point *points, int npoints, bool closed) { if (closed) { if (npoints < 3) return 0; if (pteq(points[npoints-1], points[0])) return npoints - 1; return npoints; } if (npoints < 2) return 0; return npoints - 1; } static size_t calc_series_size(int npoints) { // Make room for an extra point to ensure a perfect ring close so that // that ring->points[ring->nsegs] never overflows. // This also allows for ring calculations to be safely run on lines. npoints++; size_t size = offsetof(struct tg_ring, points); size += sizeof(struct tg_point)*(npoints < 5 ? 5 : npoints); size = aligned_size(size); return size; } static void fill_index_struct(struct index *index, int nlevels, int nsegs, int ixspread, size_t size) { // Allocate the memory needed to store the entire index plus the size // of the allocation in a pre header. index->nlevels = nlevels-1; index->memsz = size; index->spread = ixspread; // fill in the index structure fields size_t np = sizeof(struct index); np += index->nlevels*sizeof(struct level); for (int i = 0; i < index->nlevels; i++) { int nkeys = calc_num_keys(ixspread, index->nlevels-i, nsegs); index->levels[i].nrects = nkeys; index->levels[i].rects = (void*)&(((char*)index)[np]); np += nkeys*sizeof(struct ixrect); } } static struct tg_ring *series_new(const struct tg_point *points, int npoints, bool closed, enum tg_index ix) { npoints = npoints <= 0 ? 0 : npoints; int nsegs = num_segments(points, npoints, closed); size_t size = calc_series_size(npoints); int ixspread; ix = tg_index_extract_spread(ix, &ixspread); bool ystripes = false; int ixminpoints = ixspread*2; size_t ixsize = 0; int nlevels = 0; if (npoints >= ixminpoints) { bool indexed = false; if (ix == TG_DEFAULT) { ix = tg_env_get_default_index(); } switch (ix) { case TG_NATURAL: case TG_YSTRIPES: indexed = true; break; default: indexed = false; } if ((ix&TG_NONE) == TG_NONE) { // no base index } else { // use TG_NATURAL indexed = true; } if (closed && ix == TG_YSTRIPES) { // Process ystripes for closed series only. e.g. rings, not lines. ystripes = true; } if (indexed) { ixsize = calc_index_size(ixspread, nsegs, &nlevels); } } // Allocate the entire ring structure and index in a single allocation. struct tg_ring *ring = tg_malloc(size+ixsize); if (!ring) return NULL; memset(ring, 0, sizeof(struct tg_ring)); rc_init(&ring->head.rc); rc_retain(&ring->head.rc); ring->closed = closed; ring->npoints = npoints; ring->nsegs = nsegs; if (ixsize) { // size is always 64-bit aligned. // assert((size & 7) == 0); ring->index = (struct index *)(((char*)ring)+size); fill_index_struct(ring->index, nlevels, nsegs, ixspread, ixsize); } ring->rect = process_points(points, npoints, ring->points, ring->index, &ring->convex, &ring->clockwise, &ring->area); // Fill extra point to ensure perfect close. ring->points[npoints] = ring->points[0]; if (closed) { setbase(ring, BASE_RING); settype(ring, TG_POLYGON); } else { setbase(ring, BASE_LINE); settype(ring, TG_LINESTRING); } if (ystripes) { // Process ystripes for closed series only. e.g. rings, not lines. if (!process_ystripes(ring)) { tg_ring_free(ring); return NULL; } } return ring; } static struct tg_ring *series_move(const struct tg_ring *ring, bool closed, double delta_x, double delta_y) { if (!ring) return NULL; struct tg_point *points = tg_malloc(ring->npoints*sizeof(struct tg_point)); if (!points) return NULL; for (int i = 0; i < ring->npoints; i++) { points[i] = tg_point_move(ring->points[i], delta_x, delta_y); } enum tg_index ix = 0; if (ring->ystripes) { ix = TG_YSTRIPES; } else if (ring->index) { ix = TG_NATURAL; } else { ix = TG_NONE; } struct tg_ring *final = series_new(points, ring->npoints, closed, ix); tg_free(points); return final; } /// Creates a ring from a series of points. /// @param points Array of points /// @param npoints Number of points in array /// @return A newly allocated ring /// @return NULL if out of memory /// @note A tg_ring can be safely upcasted to a tg_geom. `(struct tg_geom*)ring` /// @note A tg_ring can be safely upcasted to a tg_poly. `(struct tg_poly*)ring` /// @note All rings with 32 or more points are automatically indexed. /// @see tg_ring_new_ix() /// @see RingFuncs struct tg_ring *tg_ring_new(const struct tg_point *points, int npoints) { return tg_ring_new_ix(points, npoints, TG_DEFAULT); } /// Creates a ring from a series of points using provided index option. /// @param points Array of points /// @param npoints Number of points in array /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @return A newly allocated ring /// @return NULL if out of memory /// @note A tg_ring can be safely upcasted to a tg_geom. `(struct tg_geom*)ring` /// @note A tg_ring can be safely upcasted to a tg_poly. `(struct tg_poly*)ring` /// @see tg_ring_new() /// @see [tg_index](.#tg_index) /// @see RingFuncs struct tg_ring *tg_ring_new_ix(const struct tg_point *points, int npoints, enum tg_index ix) { return series_new(points, npoints, true, ix); } /// Releases the memory associated with a ring. /// @param ring Input ring /// @see RingFuncs void tg_ring_free(struct tg_ring *ring) { if (!ring || getnoheap(ring) || !rc_release(&ring->head.rc)) { return; } if (ring->ystripes) tg_free(ring->ystripes); tg_free(ring); } static size_t ring_alloc_size(const struct tg_ring *ring) { if (ring->index) { // The index shares same allocation as the ring. return (size_t)ring->index + ring->index->memsz - (size_t)ring; } else { // There is no index. Calculate the size. return calc_series_size(ring->npoints); } } /// Clones a ring /// @param ring Input ring, caller retains ownership. /// @return A duplicate of the provided ring. /// @note The caller is responsible for freeing with tg_ring_free(). /// @note This method of cloning uses implicit sharing through an atomic /// reference counter. /// @see RingFuncs struct tg_ring *tg_ring_clone(const struct tg_ring *ring) { if (!ring || getnoheap(ring)) { return tg_ring_copy(ring); } struct tg_ring *ring_mut = (struct tg_ring*)ring; rc_retain(&ring_mut->head.rc); return ring_mut; } /// Returns the allocation size of the ring. /// @param ring Input ring /// @return Size of ring in bytes /// @see RingFuncs size_t tg_ring_memsize(const struct tg_ring *ring) { if (!ring) return 0; size_t size = ring_alloc_size(ring); if (ring->ystripes) { size += ring->ystripes->memsz; } return size; } /// Returns the number of points. /// @param ring Input ring /// @return Number of points /// @see tg_ring_point_at() /// @see RingFuncs int tg_ring_num_points(const struct tg_ring *ring) { if (!ring) return 0; return ring->npoints; } /// Returns the minimum bounding rectangle of a rect. /// @param ring Input ring /// @returns Minimum bounding rectangle /// @see RingFuncs struct tg_rect tg_ring_rect(const struct tg_ring *ring) { if (!ring) return (struct tg_rect){ 0 }; return ring->rect; } /// Returns the point at index. /// @param ring Input ring /// @param index Index of point /// @return The point at index /// @note This function performs bounds checking. Use tg_ring_points() for /// direct access to the points. /// @see tg_ring_num_points() /// @see RingFuncs struct tg_point tg_ring_point_at(const struct tg_ring *ring, int index) { if (!ring || index < 0 || index >= ring->npoints) { return (struct tg_point){ 0 }; } return ring->points[index]; } /// Returns the number of segments. /// @param ring Input ring /// @return Number of segments /// @see tg_ring_segment_at() /// @see RingFuncs int tg_ring_num_segments(const struct tg_ring *ring) { if (!ring) return 0; return ring->nsegs; } /// Returns the segment at index without bounds checking static struct tg_segment ring_segment_at(const struct tg_ring *ring, int i) { // The process_points operation ensures that there always one point more // than the number of segments. return (struct tg_segment) { ring->points[i], ring->points[i+1] }; } static struct tg_segment line_segment_at(const struct tg_line *line, int i) { return ring_segment_at((struct tg_ring*)line, i); } /// Returns the segment at index. /// @param ring Input ring /// @param index Index of segment /// @return The segment at index /// @see tg_ring_num_segments() /// @see RingFuncs struct tg_segment tg_ring_segment_at(const struct tg_ring *ring, int index) { if (!ring || index < 0 || index >= ring->nsegs) { return (struct tg_segment){ 0 }; } return ring_segment_at(ring, index); } static bool segment_rect_intersects_rect(const struct tg_segment *seg, struct tg_rect *rect) { struct tg_rect rect2; segment_fill_rect(seg, &rect2); return rect_intersects_rect(rect, &rect2); } static bool index_search(const struct tg_ring *ring, struct tg_rect *rect, int lvl, int start, bool(*iter)(struct tg_segment seg, int index, void *udata), void *udata) { const struct index *ix = ring->index; int ixspread = ix->spread; if (lvl == ix->nlevels) { int nsegs = ring->nsegs; int i = start; int e = i+ixspread; if (e > nsegs) e = nsegs; for (; i < e; i++) { int j = i; struct tg_segment *seg = (struct tg_segment *)(&ring->points[j]); if (segment_rect_intersects_rect(seg, rect)) { if (!iter(*seg, j, udata)) { return false; } } } } else { struct ixrect ixrect; tg_rect_to_ixrect(rect, &ixrect); const struct level *level = &ix->levels[lvl]; int i = start; int e = i+ixspread; if (e > level->nrects) e = level->nrects; for (; i < e; i++) { if (ixrect_intersects_ixrect(&level->rects[i], &ixrect)) { if (!index_search(ring, rect, lvl+1, i*ixspread, iter, udata)) { return false; } } } } return true; } // ring_search searches for ring segments that intersect the provided rect. void tg_ring_search(const struct tg_ring *ring, struct tg_rect rect, bool (*iter)(struct tg_segment seg, int index, void *udata), void *udata) { if (!ring || !iter) { return; } if (ring->index) { index_search(ring, &rect, 0, 0, iter, udata); } else { for (int i = 0; i < ring->nsegs; i++) { struct tg_segment *seg = (struct tg_segment *)(&ring->points[i]); if (segment_rect_intersects_rect(seg, &rect)) { if (!iter(*seg, i, udata)) return; } } } } struct ring_ring_iter_ctx { void *udata; struct tg_segment seg; int index; bool swapped; bool stop; bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg, int bidx, void *udata); }; static bool ring_ring_iter(struct tg_segment seg, int index, void *udata) { struct ring_ring_iter_ctx *ctx = udata; if (tg_segment_intersects_segment(seg, ctx->seg)) { bool ok = ctx->swapped ? ctx->iter(ctx->seg, ctx->index, seg, index, ctx->udata): ctx->iter(seg, index, ctx->seg, ctx->index, ctx->udata); if (!ok) { ctx->stop = true; return false; } } return true; } static bool ring_ring_ix(const struct tg_ring *a, int alvl, int aidx, int aspread, const struct tg_ring *b, int blvl, int bidx, int bspread, bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg, int bidx, void *udata), void *udata) { int aleaf = alvl == a->index->nlevels; int bleaf = blvl == b->index->nlevels; int anrects = aleaf ? a->nsegs : a->index->levels[alvl].nrects; int bnrects = bleaf ? b->nsegs : b->index->levels[blvl].nrects; int as = aidx; int ae = as + aspread; if (ae > anrects) ae = anrects; int bs = bidx; int be = bs + bspread; if (be > bnrects) be = bnrects; if (aleaf && bleaf) { // both are leaves for (int i = as; i < ae; i++) { struct tg_segment seg_a = ring_segment_at(a, i); for (int j = bs; j < be; j++) { struct tg_segment seg_b = ring_segment_at(b, j); if (tg_segment_intersects_segment(seg_a, seg_b)) { if (!iter(seg_a, i, seg_b, j, udata)) { return false; } } } } } else if (aleaf) { // A is a leaf and B is a branch struct tg_rect arect, brect; for (int i = as; i < ae; i++) { struct tg_segment seg = ring_segment_at(a, i); segment_fill_rect(&seg, &arect); for (int j = bs; j < be; j++) { ixrect_to_tg_rect(&b->index->levels[blvl].rects[j], &brect); if (rect_intersects_rect(&arect, &brect)) { if (!ring_ring_ix(a, alvl, i, 1, b, blvl+1, j*bspread, bspread, iter, udata)) { return false; } } } } } else if (bleaf) { // B is a leaf and A is a branch struct tg_rect arect, brect; for (int i = as; i < ae; i++) { ixrect_to_tg_rect(&a->index->levels[alvl].rects[i], &arect); for (int j = bs; j < be; j++) { struct tg_segment seg = ring_segment_at(b, j); segment_fill_rect(&seg, &brect); if (rect_intersects_rect(&arect, &brect)) { if (!ring_ring_ix(a, alvl+1, i*aspread, aspread, b, blvl, j, 1, iter, udata)) { return false; } } } } } else { // both are branches for (int i = as; i < ae; i++) { for (int j = bs; j < be; j++) { struct ixrect *arect = &a->index->levels[alvl].rects[i]; struct ixrect *brect = &b->index->levels[blvl].rects[j]; if (ixrect_intersects_ixrect(arect, brect)) { if (!ring_ring_ix(a, alvl+1, i*a->index->spread, aspread, b, blvl+1, j*b->index->spread, bspread, iter, udata)) { return false; } } } } } return true; } /// Iterates over all segments in ring A that intersect with segments in ring B. /// @note This efficiently uses the indexes of each geometry, if available. /// @see RingFuncs void tg_ring_ring_search(const struct tg_ring *a, const struct tg_ring *b, bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg, int bidx, void *udata), void *udata) { if (tg_ring_empty(a) || tg_ring_empty(b) || !iter || !tg_rect_intersects_rect(tg_ring_rect(a), tg_ring_rect(b))) { return; } if (a->index && b->index) { // both indexes are available ring_ring_ix(a, 0, 0, a->index->spread, b, 0, 0, b->index->spread, iter, udata); } else if (a->index || b->index) { // only one index is available const struct tg_ring *c = b->index ? b : a; const struct tg_ring *d = b->index ? a : b; struct ring_ring_iter_ctx ctx = { .iter = iter, .swapped = c == b, .udata = udata, }; for (int i = 0; i < d->nsegs; i++) { struct tg_segment seg = ring_segment_at(d, i); struct tg_rect rect; segment_fill_rect(&seg, &rect); ctx.seg = seg; ctx.index = i; tg_ring_search(c, rect, ring_ring_iter, &ctx); if (ctx.stop) { return; } } } else { // no indexes are available for (int i = 0; i < a->nsegs; i++) { struct tg_segment seg_a = ring_segment_at(a, i); for (int j = 0; j < b->nsegs; j++) { struct tg_segment seg_b = ring_segment_at(b, j); if (tg_segment_intersects_segment(seg_a, seg_b)) { if (!iter(seg_a, i, seg_b, j, udata)) { return; } } } } } } /// Iterates over all segments in line A that intersect with segments in line B. /// @note This efficiently uses the indexes of each geometry, if available. /// @see LineFuncs void tg_line_line_search(const struct tg_line *a, const struct tg_line *b, bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg, int bidx, void *udata), void *udata) { tg_ring_ring_search((struct tg_ring*)a, (struct tg_ring*)b, iter, udata); } /// Iterates over all segments in ring A that intersect with segments in line B. /// @note This efficiently uses the indexes of each geometry, if available. /// @see RingFuncs void tg_ring_line_search(const struct tg_ring *a, const struct tg_line *b, bool (*iter)(struct tg_segment aseg, int aidx, struct tg_segment bseg, int bidx, void *udata), void *udata) { tg_ring_ring_search(a, (struct tg_ring*)b, iter, udata); } __attr_noinline static void pip_eval_seg_slow(const struct tg_ring *ring, int i, struct tg_point point, bool allow_on_edge, bool *in, int *idx) { struct tg_segment seg = { ring->points[i], ring->points[i+1] }; switch (raycast(seg, point)) { case TG_OUT: break; case TG_IN: if (*idx == -1) { *in = !*in; } break; case TG_ON: *in = allow_on_edge; *idx = i; break; } } static inline void pip_eval_seg(const struct tg_ring *ring, int i, struct tg_point point, bool allow_on_edge, bool *in, int *idx) { // Performs fail-fast raycast boundary tests first. double ymin = fmin0(ring->points[i].y, ring->points[i+1].y); double ymax = fmax0(ring->points[i].y, ring->points[i+1].y); if (point.y < ymin || point.y > ymax) { return; } double xmin = fmin0(ring->points[i].x, ring->points[i+1].x); double xmax = fmax0(ring->points[i].x, ring->points[i+1].x); if (point.x < xmin) { if (point.y != ymin && point.y != ymax) { if (*idx != -1) return; *in = !*in; return; } } else if (point.x > xmax) { if (ymin != ymax && xmin != xmax) { return; } } pip_eval_seg_slow(ring, i, point, allow_on_edge, in, idx); } struct ring_result { bool hit; // contains/intersects int idx; // edge index }; static struct ring_result ystripes_pip(const struct tg_ring *ring, struct tg_point point, bool allow_on_edge) { bool in = false; int idx = -1; struct ystripes *ystripes = ring->ystripes; double height = ring->rect.max.y-ring->rect.min.y; int y = (point.y - ring->rect.min.y) / height * (double)ystripes->nstripes; y = fclamp0(y, 0, ystripes->nstripes-1); struct ystripe *ystripe = &ystripes->stripes[y]; for (int i = 0; i < ystripe->count; i++) { int j = ystripe->indexes[i]; pip_eval_seg(ring, j, point, allow_on_edge, &in, &idx); } return (struct ring_result){ .hit = in, .idx = idx}; } static struct ring_result standard_pip(const struct tg_ring *ring, struct tg_point point, bool allow_on_edge) { bool in = false; int idx = -1; int i = 0; while (i < ring->nsegs) { for16(i, ring->nsegs, { double ymin = fmin0(ring->points[i].y, ring->points[i+1].y); double ymax = fmax0(ring->points[i].y, ring->points[i+1].y); if (!(point.y < ymin || point.y > ymax)) { goto do_pip; } }); break; do_pip: pip_eval_seg_slow(ring, i, point, allow_on_edge, &in, &idx); i++; } return (struct ring_result){ .hit = in, .idx = idx}; } static bool branch_maybe_in(struct ixpoint point, struct ixrect rect) { if (point.y < rect.min.y || point.y > rect.max.y) { return false; } if (point.x > rect.max.x) { if (rect.min.y != rect.max.y && rect.min.x != rect.max.x) { return false; } } return true; } static void index_pip_counter(const struct tg_ring *ring, struct tg_point point, bool allow_on_edge, int lvl, int start, bool *in, int *idx) { struct index *ix = ring->index; int ixspread = ix->spread; if (lvl == ix->nlevels) { // leaf segments int i = start; int e = i+ixspread; if (e > ring->nsegs) e = ring->nsegs; for16(i, e, { pip_eval_seg(ring, i, point, allow_on_edge, in, idx); }); } else { struct ixpoint ixpoint; tg_point_to_ixpoint(&point, &ixpoint); // branch rectangles const struct level *level = &ix->levels[lvl]; int i = start; int e = i+ixspread; if (e > level->nrects) e = level->nrects; for16(i, e, { if (branch_maybe_in(ixpoint, level->rects[i])) { index_pip_counter(ring, point, allow_on_edge, lvl+1, i*ixspread, in, idx); } }); } } static struct ring_result index_pip(const struct tg_ring *ring, struct tg_point point, bool allow_on_edge) { bool in = false; int idx = -1; index_pip_counter(ring, point, allow_on_edge, 0, 0, &in, &idx); return (struct ring_result){ .hit = in, .idx = idx}; } struct ring_result tg_ring_contains_point(const struct tg_ring *ring, struct tg_point point, bool allow_on_edge) { if (!tg_rect_covers_point(ring->rect, point)) { return (struct ring_result){ .hit = false, .idx = -1 }; } if (ring->ystripes) { return ystripes_pip(ring, point, allow_on_edge); } if (ring->index) { return index_pip(ring, point, allow_on_edge); } return standard_pip(ring, point, allow_on_edge); } /// Returns true if ring is convex. /// @param ring Input ring /// @return True if ring is convex. /// @return False if ring is concave. /// @see RingFuncs bool tg_ring_convex(const struct tg_ring *ring) { if (!ring) return false; return ring->convex; } /// Returns true if winding order is clockwise. /// @param ring Input ring /// @return True if clockwise /// @return False if counter-clockwise /// @see RingFuncs bool tg_ring_clockwise(const struct tg_ring *ring) { if (!ring) return false; return ring->clockwise; } struct contsegiterctx { struct tg_segment seg; bool intersects; }; static bool contsegiter1(struct tg_segment seg2, int index, void *udata) { (void)index; struct contsegiterctx *ctx = udata; if (tg_segment_intersects_segment(ctx->seg, seg2)) { if (tg_raycast(seg2, ctx->seg.a) != TG_ON && tg_raycast(seg2, ctx->seg.b) != TG_ON) { ctx->intersects = true; return false; } } return true; } static bool contsegiter4(struct tg_segment seg2, int index, void *udata) { (void)index; struct contsegiterctx *ctx = udata; if (tg_segment_intersects_segment(ctx->seg, seg2)) { if (tg_raycast(seg2, ctx->seg.a) != TG_ON) { ctx->intersects = true; return false; } } return true; } static bool contsegiter2(struct tg_segment seg2, int index, void *udata) { (void)index; struct contsegiterctx *ctx = udata; if (tg_segment_intersects_segment(ctx->seg, seg2)) { if (tg_raycast(seg2, ctx->seg.b) != TG_ON) { ctx->intersects = true; return false; } } return true; } static bool contsegiter5(struct tg_segment seg2, int index, void *udata) { (void)index; struct contsegiterctx *ctx = udata; if (tg_segment_intersects_segment(ctx->seg, seg2)) { if (tg_raycast(ctx->seg, seg2.a) != TG_ON && tg_raycast(ctx->seg, seg2.b) != TG_ON) { ctx->intersects = true; return false; } } return true; } static bool contsegiter3(struct tg_segment seg2, int index, void *udata) { (void)index; struct contsegiterctx *ctx = udata; if (tg_segment_intersects_segment(ctx->seg, seg2)) { ctx->intersects = true; return false; } return true; } bool tg_ring_contains_segment(const struct tg_ring *ring, struct tg_segment seg, bool allow_on_edge) { if (!tg_rect_covers_rect(ring->rect, tg_segment_rect(seg))) { return false; } // Test that segment points are contained in the ring. struct ring_result res_a = tg_ring_contains_point(ring, seg.a, allow_on_edge); if (!res_a.hit) { // seg A is not inside ring return false; } if (pteq(seg.b, seg.a)) { return true; } struct ring_result res_b = tg_ring_contains_point(ring, seg.b, allow_on_edge); if (!res_b.hit) { // seg B is not inside ring return false; } if (ring->convex) { // ring is convex so the segment must be contained return true; } // The ring is concave so it's possible that the segment crosses over the // edge of the ring. if (allow_on_edge) { // do some logic around seg points that are on the edge of the ring. if (res_a.idx != -1) { // seg A is on a ring segment if (res_b.idx != -1) { // seg B is on a ring segment if (res_b.idx == res_a.idx) { // seg A and B share the same ring segment, so it must be // on the inside. return true; } // seg A and seg B are on different segments. // determine if the space that the seg passes over is inside or // outside of the ring. To do so we create a ring from the two // ring segments and check if that ring winding order matches // the winding order of the ring. // -- create a ring struct tg_segment r_seg_a = ring_segment_at(ring, res_a.idx); struct tg_segment r_seg_b = ring_segment_at(ring, res_b.idx); if (pteq(r_seg_a.a, seg.a) || pteq(r_seg_a.b, seg.a) || pteq(r_seg_b.a, seg.a) || pteq(r_seg_b.b, seg.a) || pteq(r_seg_a.a, seg.b) || pteq(r_seg_a.b, seg.b) || pteq(r_seg_b.a, seg.b) || pteq(r_seg_b.b, seg.b)) { return true; } // fix the order of the if (res_b.idx < res_a.idx) { struct tg_segment tmp = r_seg_a; r_seg_a = r_seg_b; r_seg_b = tmp; } struct tg_point pts[] = { r_seg_a.a, r_seg_a.b, r_seg_b.a, r_seg_b.b, r_seg_a.a }; // -- calc winding order double cwc = 0.0; for (int i = 0; i < 4; i++) { struct tg_point a = pts[i]; struct tg_point b = pts[i+1]; cwc += (b.x - a.x) * (b.y + a.y); } bool clockwise = cwc > 0; if (clockwise != ring->clockwise) { // -- on the outside return false; } // the passover space is on the inside of the ring. // check if seg intersects any ring segments where A and B are // not on. struct contsegiterctx ctx = { .intersects = false, .seg = seg, }; tg_ring_search(ring, tg_segment_rect(seg), contsegiter1, &ctx); return !ctx.intersects; } // case (4) // seg A is on a ring segment, but seg B is not. // check if seg intersects any ring segments where A is not on. struct contsegiterctx ctx = { .intersects = false, .seg = seg, }; tg_ring_search(ring, tg_segment_rect(seg), contsegiter4, &ctx); return !ctx.intersects; } else if (res_b.idx != -1) { // case (2) // seg B is on a ring segment, but seg A is not. // check if seg intersects any ring segments where B is not on. struct contsegiterctx ctx = { .intersects = false, .seg = seg, }; tg_ring_search(ring, tg_segment_rect(seg), contsegiter2, &ctx); return !ctx.intersects; } // case (5) (15) struct contsegiterctx ctx = { .intersects = false, .seg = seg, }; tg_ring_search(ring, tg_segment_rect(seg), contsegiter5, &ctx); return !ctx.intersects; } // allow_on_edge is false. (not allow on edge) struct contsegiterctx ctx = { .intersects = false, .seg = seg, }; tg_ring_search(ring, tg_segment_rect(seg), contsegiter3, &ctx); return !ctx.intersects; } struct intersegiterctx { struct tg_segment seg; int count; bool allow_on_edge; bool seg_a_on; bool seg_b_on; // bool yes; }; static bool intersegiter(struct tg_segment seg, int index, void *udata) { (void)index; struct intersegiterctx *ctx = udata; if (!tg_segment_intersects_segment(ctx->seg, seg)) { return true; } if (ctx->allow_on_edge) { ctx->count++; return ctx->count < 2; } struct tg_point a = ctx->seg.a; struct tg_point b = ctx->seg.b; struct tg_point c = seg.a; struct tg_point d = seg.b; // bool acol = collinear(c.x, c.y, d.x, d.y, a.x, a.y); // bool bcol = collinear(c.x, c.y, d.x, d.y, b.x, b.y); bool ccol = collinear(a.x, a.y, b.x, b.y, c.x, c.y); bool dcol = collinear(a.x, a.y, b.x, b.y, d.x, d.y); if (ccol && dcol) { // lines are parallel. ctx->count = 0; } else if (!ccol || !dcol) { if (!ctx->seg_a_on) { if (pteq(a, c) || pteq(a, d)) { ctx->seg_a_on = true; return true; } } if (!ctx->seg_b_on) { if (pteq(b, c) || pteq(b, d)) { ctx->seg_b_on = true; return true; } } ctx->count++; } return ctx->count < 2; } bool tg_ring_intersects_segment(const struct tg_ring *ring, struct tg_segment seg, bool allow_on_edge) { if (!tg_rect_intersects_rect(tg_segment_rect(seg), ring->rect)) { return false; } // Quick check that either point is inside of the ring if (tg_ring_contains_point(ring, seg.a, allow_on_edge).hit || tg_ring_contains_point(ring, seg.b, allow_on_edge).hit) { return true; } // Neither point A or B is inside of the ring. It's possible that both // are on the outside and are passing over segments. If the segment passes // over at least two ring segments then it's intersecting. struct intersegiterctx ctx = { .seg = seg, .allow_on_edge = allow_on_edge, }; tg_ring_search(ring, tg_segment_rect(seg), intersegiter, &ctx); return ctx.count >= 2; } // tg_ring_empty returns true when the ring is NULL, or does not form a closed // ring, ie. it cannot be used as a valid spatial geometry. bool tg_ring_empty(const struct tg_ring *ring) { if (!ring) return true; return (ring->closed && ring->npoints < 3) || ring->npoints < 2; } bool tg_ring_contains_ring(const struct tg_ring *a, const struct tg_ring *b, bool allow_on_edge) { if (tg_ring_empty(a) || tg_ring_empty(b)) { return false; } // test if the inner rect does not contain the outer rect if (!tg_rect_covers_rect(a->rect, b->rect)) { // not fully contained so it's not possible for the outer ring to // contain the inner ring return false; } if (a->convex) { // outer ring is convex so test that all inner points are inside of // the outer ring for (int i = 0; i < b->npoints; i++) { if (!tg_ring_contains_point(a, b->points[i], allow_on_edge).hit) { // point is on the outside the outer ring return false; } } } else { // outer ring is concave so let's make sure that all inner segments are // fully contained inside of the outer ring. for (int i = 0; i < b->nsegs; i++) { struct tg_segment seg = ring_segment_at(b, i); if (!tg_ring_contains_segment(a, seg, allow_on_edge)) { return false; } } } return true; } struct tg_rect tg_rect_move(struct tg_rect rect, double delta_x, double delta_y) { rect.min = tg_point_move(rect.min, delta_x, delta_y); rect.max = tg_point_move(rect.max, delta_x, delta_y); return rect; } static double rect_area(struct tg_rect rect) { return (rect.max.x - rect.min.x) * (rect.max.y - rect.min.y); } bool tg_ring_intersects_ring(const struct tg_ring *ring, const struct tg_ring *other, bool allow_on_edge) { if (tg_ring_empty(ring) || tg_ring_empty(other)) { return false; } // check outer and innter rects intersection first if (!tg_rect_intersects_rect(ring->rect, other->rect)) { return false; } double a1 = rect_area(tg_ring_rect(ring)); double a2 = rect_area(tg_ring_rect(other)); if (a2 > a1) { // swap the rings so that the inner ring is smaller than the outer ring const struct tg_ring *tmp = ring; ring = other; other = tmp; } for (int i = 0; i < other->nsegs; i++) { if (tg_ring_intersects_segment(ring, ring_segment_at(other, i), allow_on_edge)) { return true; } } return false; } bool tg_ring_contains_line(const struct tg_ring *a, const struct tg_line *b, bool allow_on_edge, bool respect_boundaries) { // Almost the same logic as tg_ring_contains_line except for boundaries // detection for the input line. if (tg_ring_empty(a) || tg_line_empty(b)) { return false; } // test if the inner rect does not contain the outer rect if (!tg_rect_covers_rect(a->rect, tg_line_rect(b))) { // not fully contained so it's not possible for the outer ring to // contain the inner ring return false; } // if (a->convex && !respect_boundaries) { // // outer ring is convex so test that all inner points are inside of // // the outer ring // int npoints = tg_line_num_points(b); // const struct tg_point *points = tg_line_points(b); // for (int i = 0; i < npoints; i++) { // if (!tg_ring_contains_point(a, points[i], allow_on_edge).hit) { // // point is on the outside the outer ring // return false; // } // } // } else if (!allow_on_edge && respect_boundaries) { // outer ring is concave so let's make sure that all inner segments are // fully contained inside of the outer ring. int nsegs = tg_line_num_segments(b); for (int i = 0; i < nsegs; i++) { struct tg_segment seg = line_segment_at(b, i); if (!tg_ring_contains_segment(a, seg, true)) { return false; } if (!tg_ring_intersects_segment(a, seg, false)) { return false; } } } else { // outer ring is concave so let's make sure that all inner segments are // fully contained inside of the outer ring. int nsegs = tg_line_num_segments(b); for (int i = 0; i < nsegs; i++) { struct tg_segment seg = line_segment_at(b, i); if (!tg_ring_contains_segment(a, seg, allow_on_edge)) { return false; } } } return true; } /// Tests whether a ring intersects a line. /// @see RingFuncs bool tg_ring_intersects_line(const struct tg_ring *ring, const struct tg_line *line, bool allow_on_edge) { if (tg_ring_empty(ring) || tg_line_empty(line)) { return false; } // check outer and innter rects intersection first if (!tg_rect_intersects_rect(tg_ring_rect(ring), tg_line_rect(line))) { return false; } // check if any points are inside ring // TODO: use line index if available. int nsegs = tg_line_num_segments(line); for (int i = 0; i < nsegs; i++) { if (tg_ring_intersects_segment(ring, tg_line_segment_at(line, i), allow_on_edge)) { return true; } } return false; } /// Tests whether a rectangle intersects a line. /// @see RectFuncs bool tg_rect_intersects_line(struct tg_rect rect, const struct tg_line *line) { struct tg_ring *ring = stack_ring(); rect_to_ring(rect, ring); return tg_ring_intersects_line(ring, line, true); } /// Tests whether a rectangle intersects a polygon. /// @see RectFuncs bool tg_rect_intersects_poly(struct tg_rect rect, const struct tg_poly *poly) { return tg_poly_intersects_rect(poly, rect); } /// Tests whether a rectangle fully contains a polygon. /// @see RectFuncs bool tg_rect_covers_poly(struct tg_rect rect, const struct tg_poly *poly) { return !tg_poly_empty(poly) && tg_rect_covers_rect(rect, tg_poly_rect(poly)); } struct tg_ring *tg_ring_move(const struct tg_ring *ring, double delta_x, double delta_y) { return series_move(ring, true, delta_x, delta_y); } /// Returns the underlying point array of a ring. /// @param ring Input ring /// @return Array or points /// @see tg_ring_num_points() /// @see RingFuncs const struct tg_point *tg_ring_points(const struct tg_ring *ring) { if (!ring) return NULL; return ring->points; } //////////////////// // line //////////////////// /// Creates a line from a series of points. /// @param points Array of points /// @param npoints Number of points in array /// @return A newly allocated line /// @return NULL if out of memory /// @note A tg_line can be safely upcasted to a tg_geom. `(struct tg_geom*)line` /// @note All lines with 32 or more points are automatically indexed. /// @see LineFuncs struct tg_line *tg_line_new(const struct tg_point *points, int npoints) { return tg_line_new_ix(points, npoints, TG_DEFAULT); } /// Creates a line from a series of points using provided index option. /// @param points Array of points /// @param npoints Number of points in array /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @return A newly allocated line /// @return NULL if out of memory /// @note A tg_line can be safely upcasted to a tg_geom. `(struct tg_geom*)poly` /// @see [tg_index](.#tg_index) /// @see LineFuncs struct tg_line *tg_line_new_ix(const struct tg_point *points, int npoints, enum tg_index ix) { return (struct tg_line*)series_new(points, npoints, false, ix); } /// Releases the memory associated with a line. /// @param line Input line /// @see LineFuncs void tg_line_free(struct tg_line *line) { struct tg_ring *ring = (struct tg_ring *)line; tg_ring_free(ring); } /// Returns the minimum bounding rectangle of a line. /// @see LineFuncs struct tg_rect tg_line_rect(const struct tg_line *line) { struct tg_ring *ring = (struct tg_ring *)line; return tg_ring_rect(ring); } /// Returns the number of points. /// @param line Input line /// @return Number of points /// @see tg_line_point_at() /// @see LineFuncs int tg_line_num_points(const struct tg_line *line) { struct tg_ring *ring = (struct tg_ring *)line; return tg_ring_num_points(ring); } /// Returns the point at index. /// @param line Input line /// @param index Index of point /// @return The point at index /// @note This function performs bounds checking. Use tg_line_points() for /// direct access to the points. /// @see tg_line_num_points() /// @see LineFuncs struct tg_point tg_line_point_at(const struct tg_line *line, int index) { struct tg_ring *ring = (struct tg_ring *)line; return tg_ring_point_at(ring, index); } /// Returns the number of segments. /// @param line Input line /// @return Number of segments /// @see tg_line_segment_at() /// @see LineFuncs int tg_line_num_segments(const struct tg_line *line) { struct tg_ring *ring = (struct tg_ring *)line; return tg_ring_num_segments(ring); } /// Returns the segment at index. /// @param line Input line /// @param index Index of segment /// @return The segment at index /// @see tg_line_num_segments() /// @see LineFuncs struct tg_segment tg_line_segment_at(const struct tg_line *line, int index) { struct tg_ring *ring = (struct tg_ring *)line; return tg_ring_segment_at(ring, index); } // tg_line_empty returns true when the line is NULL or has less than two // points, ie. it cannot be used as a valid spatial geometry. bool tg_line_empty(const struct tg_line *line) { struct tg_ring *ring = (struct tg_ring *)line; return tg_ring_empty(ring); } void tg_line_search(const struct tg_line *line, struct tg_rect rect, bool(*iter)(struct tg_segment seg, int index, void *udata), void *udata) { struct tg_ring *ring = (struct tg_ring *)line; tg_ring_search(ring, rect, iter, udata); } struct line_covers_point_iter_ctx { bool covers; struct tg_point point; }; static bool line_covers_point_iter(struct tg_segment seg, int index, void *udata) { (void)index; struct line_covers_point_iter_ctx *ctx = udata; if (tg_segment_covers_point(seg, ctx->point)) { ctx->covers = true; return false; } return true; } /// Tests whether a line fully contains a point /// @see LineFuncs bool tg_line_covers_point(const struct tg_line *line, struct tg_point point) { struct line_covers_point_iter_ctx ctx = { .point = point, }; tg_line_search(line, (struct tg_rect){ point, point }, line_covers_point_iter, &ctx); return ctx.covers; } bool tg_line_contains_point(const struct tg_line *line, struct tg_point point) { if (!tg_line_covers_point(line, point)) { return false; } int nsegs = tg_line_num_segments(line); if (pteq(point, tg_line_segment_at(line, 0).a) || pteq(point, tg_line_segment_at(line, nsegs-1).b)) { return false; } return true; } /// Tests whether a line intersects a point /// @see LineFuncs bool tg_line_intersects_point(const struct tg_line *line, struct tg_point point) { return tg_line_covers_point(line, point); } bool tg_line_touches_point(const struct tg_line *line, struct tg_point point) { return tg_point_touches_line(point, line); } /// Tests whether a line fully contains a rectangle /// @see LineFuncs bool tg_line_covers_rect(const struct tg_line *line, struct tg_rect rect) { // Convert rect into a poly struct tg_ring *exterior = stack_ring(); rect_to_ring(rect, exterior); struct tg_poly poly = { .exterior = exterior }; return tg_line_covers_poly(line, &poly); } /// Tests whether a line intersects a rectangle /// @see LineFuncs bool tg_line_intersects_rect(const struct tg_line *line, struct tg_rect rect) { return tg_rect_intersects_line(rect, line); } /// Tests whether a line contains another line /// @see LineFuncs bool tg_line_covers_line(const struct tg_line *a, const struct tg_line *b) { if (tg_line_empty(a) || tg_line_empty(b)) return false; if (!tg_rect_covers_rect(tg_line_rect(a), tg_line_rect(b))) { return false; } // locate the first "other" segment that contains the first "line" segment. int ansegs = tg_line_num_segments(a); int j = -1; for (int k = 0; k < ansegs; k++) { if (tg_segment_covers_segment(tg_line_segment_at(a, k), tg_line_segment_at(b, 0))) { j = k; break; } } if (j == -1) { return false; } int bnsegs = tg_line_num_segments(b); for (int i = 1; i < bnsegs && j < ansegs; i++) { struct tg_segment aseg = tg_line_segment_at(a, j); struct tg_segment bseg = tg_line_segment_at(b, i); if (tg_segment_covers_segment(aseg, bseg)) { continue; } if (pteq(bseg.a, aseg.a)) { // reverse it if (j == 0) { return false; } j--; i--; } else if (pteq(bseg.a, aseg.b)) { // forward it j++; i--; } } return true; } bool tg_line_contains_line(const struct tg_line *line, const struct tg_line *other) { return tg_line_covers_line(line, other); } enum segment_intersects_kind { SI_INTERSECTS, SI_TOUCHES, }; struct segment_intersects_iter_ctx { bool yes; int ansegs; int bnsegs; enum segment_intersects_kind kind; }; static bool segment_touches0(struct tg_segment seg, struct tg_point a, struct tg_point b) { if (!tg_segment_covers_point(seg, a)) { return false; } if (!collinear(seg.a.x, seg.a.y, seg.b.x, seg.b.y, b.x, b.y)) { return true; } if (pteq(seg.a, a)) { return !tg_segment_covers_point((struct tg_segment){ a, b }, seg.b); } if (pteq(seg.b, a)) { return !tg_segment_covers_point((struct tg_segment){ a, b }, seg.a); } return false; } static bool any_touching(struct tg_segment a, int aidx, int ansegs, struct tg_segment b, int bidx, int bnsegs) { return (aidx == 0 && segment_touches0(b, a.a, a.b)) || (aidx == ansegs-1 && segment_touches0(b, a.b, a.a)) || (bidx == 0 && segment_touches0(a, b.a, b.b)) || (bidx == bnsegs-1 && segment_touches0(a, b.b, b.a)); } static bool segment_intersects_iter(struct tg_segment a, int aidx, struct tg_segment b, int bidx, void *udata) { struct segment_intersects_iter_ctx *ctx = udata; switch (ctx->kind) { case SI_INTERSECTS: ctx->yes = true; break; case SI_TOUCHES: if (any_touching(a, aidx, ctx->ansegs, b, bidx, ctx->bnsegs)) { ctx->yes = true; return true; } ctx->yes = false; break; } return false; } static bool line_intersects_line(const struct tg_line *a, const struct tg_line *b, enum segment_intersects_kind kind) { struct segment_intersects_iter_ctx ctx = { .kind = kind, .ansegs = tg_line_num_segments(a), .bnsegs = tg_line_num_segments(b), }; tg_line_line_search(a, b, segment_intersects_iter, &ctx); return ctx.yes; } /// Tests whether a line intersects another line /// @see LineFuncs bool tg_line_intersects_line(const struct tg_line *a, const struct tg_line *b) { return line_intersects_line(a, b, SI_INTERSECTS); } bool tg_line_touches_line(const struct tg_line *a,const struct tg_line *b) { return line_intersects_line(a, b, SI_TOUCHES); } /// Tests whether a line fully contains a polygon /// @see LineFuncs bool tg_line_covers_poly(const struct tg_line *line, const struct tg_poly *poly) { if (tg_line_empty(line) || tg_poly_empty(poly)) return false; struct tg_rect rect = tg_poly_rect(poly); if (rect.min.x != rect.max.x && rect.min.y != rect.max.y) return false; // polygon can fit in a straight (vertical or horizontal) line struct tg_segment seg = { rect.min, rect.max }; struct tg_ring *other = stack_ring(); segment_to_ring(seg, other); rect_to_ring(rect, other); return tg_line_covers_line(line, (struct tg_line*)(other)); } bool tg_line_contains_poly(const struct tg_line *line, const struct tg_poly *poly) { // not possible (void)line; (void)poly; return false; } /// Tests whether a line intersects a polygon /// @see LineFuncs bool tg_line_intersects_poly(const struct tg_line *line, const struct tg_poly *poly) { return tg_poly_intersects_line(poly, line); } bool tg_line_touches_poly(const struct tg_line *a, const struct tg_poly *b) { return tg_poly_touches_line(b, a); } struct tg_line *tg_line_move(const struct tg_line *line, double delta_x, double delta_y) { const struct tg_ring *ring = (const struct tg_ring *)line; return (struct tg_line*)series_move(ring, false, delta_x, delta_y); } /// Returns true if winding order is clockwise. /// @param line Input line /// @return True if clockwise /// @return False if counter-clockwise /// @see LineFuncs bool tg_line_clockwise(const struct tg_line *line) { const struct tg_ring *ring = (const struct tg_ring *)line; return tg_ring_clockwise(ring); } /// Clones a line /// @param line Input line, caller retains ownership. /// @return A duplicate of the provided line. /// @note The caller is responsible for freeing with tg_line_free(). /// @note This method of cloning uses implicit sharing through an atomic /// reference counter. /// @see LineFuncs struct tg_line *tg_line_clone(const struct tg_line *line) { const struct tg_ring *ring = (const struct tg_ring *)line; return (struct tg_line*)tg_ring_clone(ring); } /// Returns the underlying point array of a line. /// @param line Input line /// @return Array or points /// @see tg_line_num_points() /// @see LineFuncs const struct tg_point *tg_line_points(const struct tg_line *line) { const struct tg_ring *ring = (const struct tg_ring *)line; return tg_ring_points(ring); } /// Returns the allocation size of the line. /// @param line Input line /// @return Size of line in bytes /// @see LineFuncs size_t tg_line_memsize(const struct tg_line *line) { const struct tg_ring *ring = (const struct tg_ring *)line; return tg_ring_memsize(ring); } /// Returns the indexing spread for a line. /// /// The "spread" is the number of segments or rectangles that are grouped /// together to produce a unioned rectangle that is stored at a higher level. /// /// For a tree based structure, this would be the number of items per node. /// /// @param line Input line /// @return The spread, default is 16 /// @return Zero if line has no indexing /// @see tg_line_index_num_levels() /// @see tg_line_index_level_num_rects() /// @see tg_line_index_level_rect() /// @see LineFuncs int tg_line_index_spread(const struct tg_line *line) { const struct tg_ring *ring = (const struct tg_ring *)line; return tg_ring_index_spread(ring); } /// Returns the number of levels. /// @param line Input line /// @return The number of levels /// @return Zero if line has no indexing /// @see tg_line_index_spread() /// @see tg_line_index_level_num_rects() /// @see tg_line_index_level_rect() /// @see LineFuncs int tg_line_index_num_levels(const struct tg_line *line) { const struct tg_ring *ring = (const struct tg_ring *)line; return tg_ring_index_num_levels(ring); } /// Returns the number of rectangles at level. /// @param line Input line /// @param levelidx The index of level /// @return The number of index levels /// @return Zero if line has no indexing or levelidx is out of bounds. /// @see tg_line_index_spread() /// @see tg_line_index_num_levels() /// @see tg_line_index_level_rect() /// @see LineFuncs int tg_line_index_level_num_rects(const struct tg_line *line, int levelidx) { const struct tg_ring *ring = (const struct tg_ring *)line; return tg_ring_index_level_num_rects(ring, levelidx); } /// Returns a specific level rectangle. /// @param line Input line /// @param levelidx The index of level /// @param rectidx The index of rectangle /// @return The rectangle /// @return Empty rectangle if line has no indexing, or levelidx or rectidx /// is out of bounds. /// @see tg_line_index_spread() /// @see tg_line_index_num_levels() /// @see tg_line_index_level_num_rects() /// @see LineFuncs struct tg_rect tg_line_index_level_rect(const struct tg_line *line, int levelidx, int rectidx) { const struct tg_ring *ring = (const struct tg_ring *)line; return tg_ring_index_level_rect(ring, levelidx, rectidx); } //////////////////// // poly //////////////////// /// Creates a polygon. /// @param exterior Exterior ring /// @param holes Array of interior rings that are holes /// @param nholes Number of holes in array /// @return A newly allocated polygon /// @return NULL if out of memory /// @return NULL if exterior or any holes are NULL /// @note A tg_poly can be safely upcasted to a tg_geom. `(struct tg_geom*)poly` /// @see PolyFuncs struct tg_poly *tg_poly_new(const struct tg_ring *exterior, const struct tg_ring *const holes[], int nholes) { if (!exterior) { return NULL; } if (nholes == 0) { // When the user creates a new poly without holes then we can avoid // the extra allocations by upcasting the base tg_ring to a tg_poly. return (struct tg_poly *)tg_ring_clone(exterior); } struct tg_poly *poly = tg_malloc(sizeof(struct tg_poly)); if (!poly) { goto fail; } memset(poly, 0, sizeof(struct tg_poly)); rc_init(&poly->head.rc); rc_retain(&poly->head.rc); setbase(poly, BASE_POLY); settype(poly, TG_POLYGON); poly->exterior = tg_ring_clone(exterior); if (nholes > 0) { poly->holes = tg_malloc(sizeof(struct tg_ring*)*nholes); if (!poly->holes) { goto fail; } poly->nholes = nholes; memset(poly->holes, 0, sizeof(struct tg_ring*)*poly->nholes); for (int i = 0; i < poly->nholes; i++) { poly->holes[i] = tg_ring_clone(holes[i]); } } return poly; fail: tg_poly_free(poly); return NULL; } /// Releases the memory associated with a polygon. /// @param poly Input polygon /// @see PolyFuncs void tg_poly_free(struct tg_poly *poly) { if (!poly) return; if (getbase(poly) == BASE_RING) { tg_ring_free((struct tg_ring*)poly); return; } if (getnoheap(poly) || !rc_release(&poly->head.rc)) return; if (poly->exterior) tg_ring_free(poly->exterior); if (poly->holes) { for (int i = 0; i < poly->nholes; i++) { if (poly->holes[i]) tg_ring_free(poly->holes[i]); } tg_free(poly->holes); } tg_free(poly); } /// Clones a polygon. /// @param poly Input polygon, caller retains ownership. /// @return A duplicate of the provided polygon. /// @note The caller is responsible for freeing with tg_poly_free(). /// @note This method of cloning uses implicit sharing through an atomic /// reference counter. /// @see PolyFuncs struct tg_poly *tg_poly_clone(const struct tg_poly *poly) { if (!poly || getnoheap(poly)) { return tg_poly_copy(poly); } struct tg_poly *poly_mut = (struct tg_poly*)poly; rc_retain(&poly_mut->head.rc); return poly_mut; } /// Returns the exterior ring. /// @param poly Input polygon /// @return Exterior ring /// @note The polygon maintains ownership of the exterior ring. /// @see PolyFuncs const struct tg_ring *tg_poly_exterior(const struct tg_poly *poly) { if (!poly) return NULL; if (getbase(poly) == BASE_RING) { return (struct tg_ring*)poly; } return poly->exterior; } /// Returns the number of interior holes. /// @param poly Input polygon /// @return Number of holes /// @see tg_poly_hole_at() /// @see PolyFuncs int tg_poly_num_holes(const struct tg_poly *poly) { if (!poly || getbase(poly) == BASE_RING) return 0; return poly->nholes; } /// Returns an interior hole. /// @param poly Input polygon /// @param index Index of hole /// @return Ring hole /// @see tg_poly_num_holes() /// @see PolyFuncs const struct tg_ring *tg_poly_hole_at(const struct tg_poly *poly, int index) { if (!poly || getbase(poly) == BASE_RING) return NULL; if (index < 0 || index >= poly->nholes) return NULL; return poly->holes[index]; } /// Returns true if winding order is clockwise. /// @param poly Input polygon /// @return True if clockwise /// @return False if counter-clockwise /// @see PolyFuncs bool tg_poly_clockwise(const struct tg_poly *poly) { return tg_ring_clockwise(tg_poly_exterior(poly)); } /// Returns true if polygon is empty. /// @see PolyFuncs bool tg_poly_empty(const struct tg_poly *poly) { return tg_ring_empty(tg_poly_exterior(poly)); } /// Returns the minimum bounding rectangle of a polygon. /// @see PolyFuncs struct tg_rect tg_poly_rect(const struct tg_poly *poly) { return tg_ring_rect(tg_poly_exterior(poly)); } static bool poly_contains_point(const struct tg_poly *poly, struct tg_point point, bool allow_on_edge) { if (poly && getbase(poly) == BASE_RING) { // downcast fast path return tg_ring_contains_point((struct tg_ring*)poly, point, allow_on_edge).hit; } // standard path if (tg_poly_empty(poly)) { return false; } if (!tg_ring_contains_point(poly->exterior, point, allow_on_edge).hit) { return false; } bool covers = true; for (int i = 0; i < poly->nholes; i++) { if (tg_ring_contains_point(poly->holes[i], point, !allow_on_edge).hit) { covers = false; break; } } return covers; } /// Tests whether a polygon fully contains a point. /// @see PolyFuncs bool tg_poly_covers_point(const struct tg_poly *poly, struct tg_point point) { return poly_contains_point(poly, point, true); } bool tg_poly_contains_point(const struct tg_poly *poly, struct tg_point point) { return poly_contains_point(poly, point, false); } /// Tests whether a polygon fully contains a point using xy coordinates. /// @see PolyFuncs bool tg_poly_covers_xy(const struct tg_poly *poly, double x, double y) { return tg_poly_covers_point(poly, (struct tg_point){ .x = x, .y = y }); } /// Tests whether a polygon intersects a point. /// @see PolyFuncs bool tg_poly_intersects_point(const struct tg_poly *poly, struct tg_point point) { return tg_poly_covers_point(poly, point); } bool tg_poly_touches_point(const struct tg_poly *poly, struct tg_point point) { return tg_point_touches_poly(point, poly); } /// Tests whether a polygon fully contains a rectangle. /// @see PolyFuncs bool tg_poly_covers_rect(const struct tg_poly *poly, struct tg_rect rect) { // Convert rect into a poly struct tg_ring *other = stack_ring(); rect_to_ring(rect, other); return tg_poly_covers_poly(poly, (struct tg_poly*)other); } /// Tests whether a polygon intersects a rectangle. /// @see PolyFuncs bool tg_poly_intersects_rect(const struct tg_poly *poly, struct tg_rect rect) { // convert rect into a poly struct tg_ring *other = stack_ring(); rect_to_ring(rect, other); return tg_poly_intersects_poly(poly, (struct tg_poly*)other); } /// Tests whether a polygon covers (fully contains) a line. /// @see PolyFuncs bool tg_poly_covers_line(const struct tg_poly *a, const struct tg_line *b) { if (tg_poly_empty(a) || tg_line_empty(b)) { return false; } if (!tg_ring_contains_line(tg_poly_exterior(a), b, true, false)) { return false; } int nholes = tg_poly_num_holes(a); for (int i = 0; i < nholes; i++) { if (tg_ring_intersects_line(tg_poly_hole_at(a, i), b, false)) { return false; } } return true; } bool tg_poly_contains_line(const struct tg_poly *a, const struct tg_line *b) { if (tg_poly_empty(a) || tg_line_empty(b)) { return false; } if (!tg_ring_contains_line(tg_poly_exterior(a), b, false, true)) { return false; } int nholes = tg_poly_num_holes(a); for (int i = 0; i < nholes; i++) { if (tg_ring_intersects_line(tg_poly_hole_at(a, i), b, false)) { return false; } } return true; } /// Tests whether a polygon intersects a line. /// @see PolyFuncs bool tg_poly_intersects_line(const struct tg_poly *poly, const struct tg_line *line) { if (poly && getbase(poly) == BASE_RING) { // downcast fast path return tg_ring_intersects_line((struct tg_ring*)poly, line, true); } // standard path if (tg_poly_empty(poly) || tg_line_empty(line)) { return false; } if (!tg_ring_intersects_line(poly->exterior, line, true)) { return false; } for (int i = 0; i < poly->nholes; i++) { if (tg_ring_contains_line(poly->holes[i], line, false, false)) { return false; } } return true; } /// Tests whether a polygon fully contains another polygon. /// @see PolyFuncs bool tg_poly_covers_poly(const struct tg_poly *a, const struct tg_poly *b) { if (a && getbase(a) == BASE_RING && b && getbase(b) == BASE_RING) { // downcast fast path return tg_ring_contains_ring((struct tg_ring*)a, (struct tg_ring*)b, true); } // standard path if (tg_poly_empty(a) || tg_poly_empty(b)) { return false; } const struct tg_ring *a_exterior = tg_poly_exterior(a); const struct tg_ring *b_exterior = tg_poly_exterior(b); int a_nholes = tg_poly_num_holes(a); int b_nholes = tg_poly_num_holes(b); struct tg_ring **a_holes = NULL; if (getbase(a) == BASE_POLY) { a_holes = a->holes; } struct tg_ring **b_holes = NULL; if (getbase(b) == BASE_POLY) { b_holes = b->holes; } // 1) other exterior must be fully contained inside of the poly exterior. if (!tg_ring_contains_ring(a_exterior, b_exterior, true)) { return false; } // 2) ring cannot intersect poly holes bool covers = true; for (int i = 0; i < a_nholes; i++) { if (tg_ring_intersects_ring(a_holes[i], b_exterior, false)) { covers = false; // 3) unless the poly hole is contain inside of a other hole for (int j = 0; j < b_nholes; j++) { if (tg_ring_contains_ring(b_holes[j], a_holes[i], true)) { covers = true; break; } } if (!covers) { break; } } } return covers; } bool tg_poly_contains_poly(const struct tg_poly *a, const struct tg_poly *b) { return tg_poly_covers_poly(a, b); } /// Tests whether a polygon intesects a polygon. /// @see PolyFuncs bool tg_poly_intersects_poly(const struct tg_poly *poly, const struct tg_poly *other) { if (poly && getbase(poly) == BASE_RING && other && getbase(other) == BASE_RING) { // downcast fast path return tg_ring_intersects_ring((struct tg_ring*)poly, (struct tg_ring*)other, true); } // standard path if (tg_poly_empty(poly) || tg_poly_empty(other)) return false; const struct tg_ring *poly_exterior = tg_poly_exterior(poly); const struct tg_ring *other_exterior = tg_poly_exterior(other); int poly_nholes = tg_poly_num_holes(poly); int other_nholes = tg_poly_num_holes(other); struct tg_ring **poly_holes = NULL; if (getbase(poly) == BASE_POLY) poly_holes = poly->holes; struct tg_ring **other_holes = NULL; if (getbase(other) == BASE_POLY) other_holes = other->holes; if (!tg_ring_intersects_ring(other_exterior, poly_exterior, true)) { return false; } for (int i = 0; i < poly_nholes; i++) { if (tg_ring_contains_ring(poly_holes[i], other_exterior, false)) { return false; } } for (int i = 0; i < other_nholes; i++) { if (tg_ring_contains_ring(other_holes[i], poly_exterior, false)) { return false; } } return true; } bool tg_poly_touches_line(const struct tg_poly *a, const struct tg_line *b) { if (!tg_rect_intersects_rect(tg_poly_rect(a), tg_line_rect(b))) { return false; } // Check if the line is inside any of the polygon holes int npoints = tg_line_num_points(b); int nholes = tg_poly_num_holes(a); for (int i = 0; i < nholes; i++) { const struct tg_ring *hole = tg_poly_hole_at(a, i); if (tg_ring_contains_line(hole, b, true, false)) { // Yes, now check if any of the points touch the hole boundary. for (int j = 0; j < npoints; j++) { struct tg_point point = tg_line_point_at(b, j); if (tg_line_covers_point((struct tg_line*)hole, point)) { return true; } } return false; } } // Check if at least one line point touches the polygon exterior. const struct tg_ring *ring = tg_poly_exterior(a); bool touches = false; for (int i = 0; i < npoints; i++) { struct tg_point point = tg_line_point_at(b, i); // Cast the exterior ring to a polygon to avoid holes. if (tg_poly_touches_point((struct tg_poly*)ring, point)) { touches = true; break; } } if (!touches) { return false; } int nsegs = tg_line_num_segments(b); for (int i = 0; i < nsegs; i++) { struct tg_segment seg = tg_line_segment_at(b, i); if (tg_ring_intersects_segment(ring, seg, false)) { return false; } } return true; } bool tg_poly_touches_poly(const struct tg_poly *a, const struct tg_poly *b) { if (!tg_rect_intersects_rect(tg_poly_rect(a), tg_poly_rect(b))) { return false; } const struct tg_ring *aext = tg_poly_exterior(a); const struct tg_ring *bext = tg_poly_exterior(b); // Check if one polygon is fully inside a hole of the other and touching // the hole boundary. for (int ii = 0; ii < 2; ii++) { const struct tg_poly *poly = ii == 0 ? a : b; const struct tg_ring *ring = ii == 0 ? bext : aext; int nholes = tg_poly_num_holes(poly); for (int i = 0; i < nholes; i++) { const struct tg_ring *hole = tg_poly_hole_at(poly, i); if (tg_ring_contains_ring(hole, ring, true)) { // Yes, now check if any exterior points are on the other // hole boundary. int npoints = tg_ring_num_points(ring); for (int j = 0; j < npoints; j++) { struct tg_point point = tg_ring_point_at(ring, j); if (tg_line_covers_point((struct tg_line*)hole, point)) { // Touching return true; } } // Not touching and full enclosed in a hole. return false; } } } // Now we can work with the exterior rings only. // Check if one polygon is touching the other int ansegs = tg_ring_num_segments(aext); int bnsegs = tg_ring_num_segments(bext); int atouches = 0; int btouches = 0; for (int ii = 0; ii < 2; ii++) { const struct tg_ring *a = ii == 0 ? aext : bext; const struct tg_ring *b = ii == 0 ? bext : aext; int nsegs = ii == 0 ? bnsegs : ansegs; int touches = 0; for (int i = 0; i < nsegs; i++) { struct tg_segment seg = tg_ring_segment_at(b, i); bool isects0 = tg_ring_intersects_segment(a, seg, true); bool isects1 = tg_ring_intersects_segment(a, seg, false); if (isects0 && !isects1) { touches++; } else if (isects0 || isects1) { return false; } } if (ii == 0) { btouches = touches; } else { atouches = touches; } } if (atouches > 0 || btouches > 0) { return !(atouches == ansegs && btouches == bnsegs); } return false; } struct tg_poly *tg_poly_move(const struct tg_poly *poly, double delta_x, double delta_y) { if (!poly) return NULL; if (getbase(poly) == BASE_RING) { return (struct tg_poly*)tg_ring_move((struct tg_ring*)poly, delta_x, delta_y); } struct tg_poly *final = NULL; struct tg_ring *exterior = NULL; struct tg_ring **holes = NULL; if (poly->exterior) { exterior = tg_ring_move(poly->exterior, delta_x, delta_y); if (!exterior) goto done; } if (poly->nholes > 0) { holes = tg_malloc(sizeof(struct tg_ring*)*poly->nholes); if (!holes) goto done; memset(holes, 0, sizeof(struct tg_ring*)*poly->nholes); for (int i = 0; i < poly->nholes; i++) { holes[i] = tg_ring_move(poly->holes[i], delta_x, delta_y); if (!holes[i]) goto done; } } final = tg_poly_new(exterior, (const struct tg_ring**)holes, poly->nholes); done: if (exterior) tg_ring_free(exterior); if (holes) { for (int i = 0; i < poly->nholes; i++) { if (holes[i]) tg_ring_free(holes[i]); } tg_free(holes); } return final; } /// Returns the allocation size of the polygon. /// @param poly Input polygon /// @return Size of polygon in bytes /// @see PolyFuncs size_t tg_poly_memsize(const struct tg_poly *poly) { if (!poly) return 0; if (getbase(poly) == BASE_RING) { return tg_ring_memsize((struct tg_ring*)poly); } size_t size = sizeof(struct tg_poly); if (poly->exterior) { size += tg_ring_memsize(poly->exterior); } size += poly->nholes*sizeof(struct tg_ring); for (int i = 0; i < poly->nholes; i++) { size += tg_ring_memsize(poly->holes[i]); } return size; } //////////////////// // geom //////////////////// static struct tg_geom *geom_new(enum tg_geom_type type) { struct tg_geom *geom = tg_malloc(sizeof(struct tg_geom)); if (!geom) return NULL; memset(geom, 0, sizeof(struct tg_geom)); rc_init(&geom->head.rc); rc_retain(&geom->head.rc); setbase(geom, BASE_GEOM); settype(geom, type); return geom; } static struct tg_geom *geom_new_empty(enum tg_geom_type type) { struct tg_geom *geom = geom_new(type); if (!geom) return NULL; setflags(geom, IS_EMPTY); return geom; } /// Creates a Point geometry. /// @param point Input point /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructors struct tg_geom *tg_geom_new_point(struct tg_point point) { struct boxed_point *geom = tg_malloc(sizeof(struct boxed_point)); if (!geom) return NULL; memset(geom, 0, sizeof(struct boxed_point)); rc_init(&geom->head.rc); rc_retain(&geom->head.rc); setbase(geom, BASE_POINT); settype(geom, TG_POINT); geom->point = point; return (struct tg_geom*)geom; } static void boxed_point_free(struct boxed_point *point) { if (getnoheap(point) || !rc_release(&point->head.rc)) { return; } tg_free(point); } /// Creates a Point geometry that includes a Z coordinate. /// @param point Input point /// @param z The Z coordinate /// @return A newly allocated geometry, or NULL if system is out of /// memory. The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_point_z(struct tg_point point, double z) { struct tg_geom *geom = geom_new(TG_POINT); if (!geom) return NULL; setflags(geom, HAS_Z); geom->point = point; geom->z = z; return geom; } /// Creates a Point geometry that includes an M coordinate. /// @param point Input point /// @param m The M coordinate /// @return A newly allocated geometry, or NULL if system is out of /// memory. The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_point_m(struct tg_point point, double m) { struct tg_geom *geom = geom_new(TG_POINT); if (!geom) return NULL; setflags(geom, HAS_M); geom->point = point; geom->m = m; return geom; } /// Creates a Point geometry that includes a Z and M coordinates. /// @param point Input point /// @param z The Z coordinate /// @param m The M coordinate /// @return A newly allocated geometry, or NULL if system is out of /// memory. The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_point_zm(struct tg_point point, double z, double m) { struct tg_geom *geom = geom_new(TG_POINT); if (!geom) return NULL; setflags(geom, HAS_Z | HAS_M); geom->point = point; geom->z = z; geom->m = m; return geom; } /// Creates an empty Point geometry. /// @return A newly allocated geometry, or NULL if system is out of /// memory. The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_point_empty(void) { return geom_new_empty(TG_POINT); } /// Creates a LineString geometry. /// @param line Input line, caller retains ownership. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructors struct tg_geom *tg_geom_new_linestring(const struct tg_line *line) { return (struct tg_geom*)tg_line_clone(line); } /// Creates an empty LineString geometry. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_linestring_empty(void) { return geom_new_empty(TG_LINESTRING); } /// Creates a Polygon geometry. /// @param poly Input polygon, caller retains ownership. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructors struct tg_geom *tg_geom_new_polygon(const struct tg_poly *poly) { return (struct tg_geom*)tg_poly_clone(poly); } /// Creates an empty Polygon geometry. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_polygon_empty(void) { return geom_new_empty(TG_POLYGON); } static struct tg_geom *geom_new_multi(enum tg_geom_type type, int ngeoms) { ngeoms = ngeoms < 0 ? 0 : ngeoms; struct tg_geom *geom = geom_new(type); if (!geom) return NULL; geom->multi = tg_malloc(sizeof(struct multi)); if (!geom->multi) { tg_free(geom); return NULL; } memset(geom->multi, 0, sizeof(struct multi)); geom->multi->geoms = tg_malloc(ngeoms*sizeof(struct tg_geom*)); if (!geom->multi->geoms) { tg_free(geom->multi); tg_free(geom); return NULL; } memset(geom->multi->geoms, 0, ngeoms*sizeof(struct tg_geom*)); geom->multi->ngeoms = ngeoms; const int spread = 32; if (ngeoms >= spread*2) { int nlevels; size_t ixsize = calc_index_size(spread, ngeoms, &nlevels); geom->multi->index = tg_malloc(ixsize); geom->multi->ixgeoms = tg_malloc(ngeoms*sizeof(int)); if (!geom->multi->index || !geom->multi->ixgeoms) { if (geom->multi->index) tg_free(geom->multi->index); if (geom->multi->ixgeoms) tg_free(geom->multi->ixgeoms); tg_free(geom->multi->geoms); tg_free(geom->multi); tg_free(geom); return NULL; } fill_index_struct(geom->multi->index, nlevels, ngeoms, spread, ixsize); } return geom; } // Fast 2D hilbert curve // https://github.com/rawrunprotected/hilbert_curves // Public Domain static uint32_t hilbert_xy_to_index(uint32_t x, uint32_t y) { uint32_t A, B, C, D; uint32_t a, b, c, d; // Round (1) Initial prefix scan round, prime with x and y a = x ^ y; b = 0xFFFF ^ a; c = 0xFFFF ^ (x | y); d = x & (y ^ 0xFFFF); A = a | (b >> 1); B = (a >> 1) ^ a; C = ((c >> 1) ^ (b & (d >> 1))) ^ c; D = ((a & (c >> 1)) ^ (d >> 1)) ^ d; // Round (2) a = A; b = B; c = C; d = D; A = ((a & (a >> 2)) ^ (b & (b >> 2))); B = ((a & (b >> 2)) ^ (b & ((a ^ b) >> 2))); C ^= ((a & (c >> 2)) ^ (b & (d >> 2))); D ^= ((b & (c >> 2)) ^ ((a ^ b) & (d >> 2))); // Round (3) a = A; b = B; c = C; d = D; A = ((a & (a >> 4)) ^ (b & (b >> 4))); B = ((a & (b >> 4)) ^ (b & ((a ^ b) >> 4))); C ^= ((a & (c >> 4)) ^ (b & (d >> 4))); D ^= ((b & (c >> 4)) ^ ((a ^ b) & (d >> 4))); // Round (4) Final round and projection a = A; b = B; c = C; d = D; C ^= ((a & (c >> 8)) ^ (b & (d >> 8))); D ^= ((b & (c >> 8)) ^ ((a ^ b) & (d >> 8))); // Undo transformation prefix scan a = C ^ (C >> 1); b = D ^ (D >> 1); // Recover index bits uint32_t i0 = x ^ y; uint32_t i1 = b | (0xFFFF ^ (i0 | a)); // Interleave (i0) i0 = (i0 | (i0 << 8)) & 0x00FF00FF; i0 = (i0 | (i0 << 4)) & 0x0F0F0F0F; i0 = (i0 | (i0 << 2)) & 0x33333333; i0 = (i0 | (i0 << 1)) & 0x55555555; // Interleave (i1) i1 = (i1 | (i1 << 8)) & 0x00FF00FF; i1 = (i1 | (i1 << 4)) & 0x0F0F0F0F; i1 = (i1 | (i1 << 2)) & 0x33333333; i1 = (i1 | (i1 << 1)) & 0x55555555; return (i1 << 1) | i0; } uint32_t tg_point_hilbert(struct tg_point point, struct tg_rect rect) { uint32_t ix = ((point.x - rect.min.x) / (rect.max.x - rect.min.x)) * 0xFFFF; uint32_t iy = ((point.y - rect.min.y) / (rect.max.y - rect.min.y)) * 0xFFFF; return hilbert_xy_to_index(ix, iy); } struct hildex { uint32_t hilbert; int index; }; static int hilsort(const void *a, const void *b) { const struct hildex *ha = a; const struct hildex *hb = b; return ha->hilbert < hb->hilbert ? -1 : ha->hilbert > hb->hilbert; } static bool multi_geom_inflate_index(struct multi *multi) { // inflate multi index base level and mbr in one pass struct index *index = multi->index; int nlevels = index->nlevels; int spread = index->spread; // fill the hilbert indexes struct hildex *hildexes = tg_malloc(sizeof(struct hildex)*multi->ngeoms); if (!hildexes) { return false; } for (int i = 0; i < multi->ngeoms; i++) { struct tg_point center = tg_rect_center(tg_geom_rect(multi->geoms[i])); hildexes[i].index = i; hildexes[i].hilbert = tg_point_hilbert(center, multi->rect); } qsort(hildexes, multi->ngeoms, sizeof(struct hildex), hilsort); for (int i = 0; i < multi->ngeoms; i++) { multi->ixgeoms[i] = hildexes[i].index; } tg_free(hildexes); struct ixrect ixrect; struct tg_rect rect0 = tg_geom_rect(multi->geoms[multi->ixgeoms[0]]); tg_rect_to_ixrect(&rect0, &ixrect); int j = 1; int k = 0; for (int i = 1; i < multi->ngeoms; i++) { struct tg_rect rect = tg_geom_rect(multi->geoms[multi->ixgeoms[i]]); multi->rect = tg_rect_expand(multi->rect, rect); struct ixrect ixrect2; tg_rect_to_ixrect(&rect, &ixrect2); if (j == spread) { index->levels[nlevels-1].rects[k] = ixrect; k++; j = 1; ixrect = ixrect2; } else { ixrect_expand(&ixrect, &ixrect2); j++; } } if (k < index->levels[nlevels-1].nrects) { index->levels[nlevels-1].rects[k] = ixrect; k++; } for (int lvl = nlevels-1; lvl > 0; lvl--) { struct level *level = &index->levels[lvl]; struct ixrect ixrect = level->rects[0]; int j = 1; int k = 0; for (int i = 1; i < level->nrects; i++) { if (j == spread) { index->levels[lvl-1].rects[k] = ixrect; k++; j = 1; ixrect = index->levels[lvl].rects[i]; } else { ixrect_expand(&ixrect, &index->levels[lvl].rects[i]); j++; } } if (k < index->levels[lvl-1].nrects) { index->levels[lvl-1].rects[k] = ixrect; k++; } } return true; } static struct tg_geom *multi_geom_inflate_rect(struct tg_geom *geom) { if (geom->multi->ngeoms == 0) { geom->multi->rect = (struct tg_rect){ 0 }; return geom; } geom->multi->rect = tg_geom_rect(geom->multi->geoms[0]); for (int i = 1; i < geom->multi->ngeoms; i++) { struct tg_rect rect = tg_geom_rect(geom->multi->geoms[i]); geom->multi->rect = tg_rect_expand(geom->multi->rect, rect); } if (geom->multi->index) { if (!multi_geom_inflate_index(geom->multi)) { tg_geom_free(geom); return NULL; } } return geom; } static const struct multi *geom_multi(const struct tg_geom *geom) { if (geom && getbase(geom) == BASE_GEOM && ( gettype(geom) == TG_MULTIPOINT || gettype(geom) == TG_MULTILINESTRING || gettype(geom) == TG_MULTIPOLYGON || gettype(geom) == TG_GEOMETRYCOLLECTION)) { return geom->multi; } return NULL; } static const struct index *geom_multi_index(const struct tg_geom *geom) { const struct multi *multi = geom_multi(geom); return multi ? multi->index : NULL; } int tg_geom_multi_index_spread(const struct tg_geom *geom) { const struct index *index = geom_multi_index(geom); return index_spread(index); } int tg_geom_multi_index_num_levels(const struct tg_geom *geom) { const struct index *index = geom_multi_index(geom); return index_num_levels(index); } int tg_geom_multi_index_level_num_rects(const struct tg_geom *geom, int levelidx) { const struct index *index = geom_multi_index(geom); return index_level_num_rects(index, levelidx); } struct tg_rect tg_geom_multi_index_level_rect(const struct tg_geom *geom, int levelidx, int rectidx) { const struct index *index = geom_multi_index(geom); return index_level_rect(index, levelidx, rectidx); } /// Creates a MultiPoint geometry. /// @param points An array of points, caller retains ownership. /// @param npoints The number of points in array /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructors struct tg_geom *tg_geom_new_multipoint(const struct tg_point *points, int npoints) { struct tg_geom *geom = geom_new_multi(TG_MULTIPOINT, npoints); if (!geom) return NULL; for (int i = 0; i < geom->multi->ngeoms; i++) { geom->multi->geoms[i] = tg_geom_new_point(points[i]); if (!geom->multi->geoms[i]) { tg_geom_free(geom); return NULL; } } return multi_geom_inflate_rect(geom); } /// Creates an empty MultiPoint geometry. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multipoint_empty(void) { return geom_new_empty(TG_MULTIPOINT); } /// Creates a MultiLineString geometry. /// @param lines An array of lines, caller retains ownership. /// @param nlines The number of lines in array /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructors struct tg_geom *tg_geom_new_multilinestring(const struct tg_line *const lines[], int nlines) { struct tg_geom *geom = geom_new_multi(TG_MULTILINESTRING, nlines); if (!geom) return NULL; for (int i = 0; i < geom->multi->ngeoms; i++) { geom->multi->geoms[i] = (struct tg_geom*)tg_line_clone(lines[i]); } return multi_geom_inflate_rect(geom); } /// Creates an empty MultiLineString geometry. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multilinestring_empty(void) { return geom_new_empty(TG_MULTILINESTRING); } /// Creates a MultiPolygon geometry. /// @param polys An array of polygons, caller retains ownership. /// @param npolys The number of polygons in array /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructors struct tg_geom *tg_geom_new_multipolygon(const struct tg_poly *const polys[], int npolys) { struct tg_geom *geom = geom_new_multi(TG_MULTIPOLYGON, npolys); if (!geom) return NULL; for (int i = 0; i < geom->multi->ngeoms; i++) { geom->multi->geoms[i] = (struct tg_geom*)tg_poly_clone(polys[i]); } return multi_geom_inflate_rect(geom); } /// Creates an empty MultiPolygon geometry. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multipolygon_empty(void) { return geom_new_empty(TG_MULTIPOLYGON); } /// Creates a GeometryCollection geometry. /// @param geoms An array of geometries, caller retains ownership. /// @param ngeoms The number of geometries in array /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructors struct tg_geom *tg_geom_new_geometrycollection( const struct tg_geom *const geoms[], int ngeoms) { struct tg_geom *geom = geom_new_multi(TG_GEOMETRYCOLLECTION, ngeoms); if (!geom) return NULL; for (int i = 0; i < geom->multi->ngeoms; i++) { geom->multi->geoms[i] = tg_geom_clone(geoms[i]); } return multi_geom_inflate_rect(geom); } /// Creates an empty GeometryCollection geometry. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_geometrycollection_empty(void) { return geom_new_empty(TG_GEOMETRYCOLLECTION); } static struct tg_geom *fill_extra_coords(struct tg_geom *geom, const double *coords, int ncoords, enum flags flags) { ncoords = ncoords < 0 ? 0 : ncoords; // if (!geom) return NULL; // already checked setflags(geom, flags); geom->ncoords = ncoords; if (ncoords == 0) { geom->coords = NULL; } else { geom->coords = tg_malloc(ncoords*sizeof(double)); if (!geom->coords) { tg_geom_free(geom); return NULL; } memcpy(geom->coords, coords, ncoords*sizeof(double)); } return geom; } /// Creates a LineString geometry that includes Z coordinates. /// @param line Input line, caller retains ownership. /// @param coords Array of doubles representing each Z coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_linestring_z(const struct tg_line *line, const double *coords, int ncoords) { struct tg_geom *geom = geom_new(TG_LINESTRING); if (!geom) return NULL; geom->line = tg_line_clone(line); return fill_extra_coords(geom, coords, ncoords, HAS_Z); } /// Creates a LineString geometry that includes M coordinates. /// @param line Input line, caller retains ownership. /// @param coords Array of doubles representing each M coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_linestring_m(const struct tg_line *line, const double *coords, int ncoords) { struct tg_geom *geom = geom_new(TG_LINESTRING); if (!geom) return NULL; geom->line = tg_line_clone(line); return fill_extra_coords(geom, coords, ncoords, HAS_M); } /// Creates a LineString geometry that includes ZM coordinates. /// @param line Input line, caller retains ownership. /// @param coords Array of doubles representing each Z and M coordinate, /// interleaved. Caller retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_linestring_zm(const struct tg_line *line, const double *coords, int ncoords) { struct tg_geom *geom = geom_new(TG_LINESTRING); if (!geom) return NULL; geom->line = tg_line_clone(line); return fill_extra_coords(geom, coords, ncoords, HAS_Z|HAS_M); } /// Creates a Polygon geometry that includes Z coordinates. /// @param poly Input polygon, caller retains ownership. /// @param coords Array of doubles representing each Z coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_polygon_z(const struct tg_poly *poly, const double *coords, int ncoords) { struct tg_geom *geom = geom_new(TG_POLYGON); if (!geom) return NULL; geom->poly = tg_poly_clone(poly); return fill_extra_coords(geom, coords, ncoords, HAS_Z); } /// Creates a Polygon geometry that includes M coordinates. /// @param poly Input polygon, caller retains ownership. /// @param coords Array of doubles representing each M coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_polygon_m(const struct tg_poly *poly, const double *coords, int ncoords) { struct tg_geom *geom = geom_new(TG_POLYGON); if (!geom) return NULL; geom->poly = tg_poly_clone(poly); return fill_extra_coords(geom, coords, ncoords, HAS_M); } /// Creates a Polygon geometry that includes ZM coordinates. /// @param poly Input polygon, caller retains ownership. /// @param coords Array of doubles representing each Z and M coordinate, /// interleaved. Caller retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_polygon_zm(const struct tg_poly *poly, const double *coords, int ncoords) { struct tg_geom *geom = geom_new(TG_POLYGON); if (!geom) return NULL; geom->poly = tg_poly_clone(poly); return fill_extra_coords(geom, coords, ncoords, HAS_Z|HAS_M); } /// Creates a MultiPoint geometry that includes Z coordinates. /// @param points An array of points, caller retains ownership. /// @param npoints The number of points in array /// @param coords Array of doubles representing each Z coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multipoint_z(const struct tg_point *points, int npoints, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multipoint(points, npoints); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_Z); } /// Creates a MultiPoint geometry that includes M coordinates. /// @param points An array of points, caller retains ownership. /// @param npoints The number of points in array /// @param coords Array of doubles representing each M coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multipoint_m(const struct tg_point *points, int npoints, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multipoint(points, npoints); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_M); } /// Creates a MultiPoint geometry that includes ZM coordinates. /// @param points An array of points, caller retains ownership. /// @param npoints The number of points in array /// @param coords Array of doubles representing each Z and M coordinate, /// interleaved. Caller retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multipoint_zm(const struct tg_point *points, int npoints, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multipoint(points, npoints); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_Z|HAS_M); } /// Creates a MultiLineString geometry that includes Z coordinates. /// @param lines An array of lines, caller retains ownership. /// @param nlines The number of lines in array /// @param coords Array of doubles representing each Z coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multilinestring_z( const struct tg_line *const lines[], int nlines, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multilinestring(lines, nlines); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_Z); } /// Creates a MultiLineString geometry that includes M coordinates. /// @param lines An array of lines, caller retains ownership. /// @param nlines The number of lines in array /// @param coords Array of doubles representing each M coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multilinestring_m( const struct tg_line *const lines[], int nlines, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multilinestring(lines, nlines); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_M); } /// Creates a MultiLineString geometry that includes ZM coordinates. /// @param lines An array of lines, caller retains ownership. /// @param nlines The number of lines in array /// @param coords Array of doubles representing each Z and M coordinate, /// interleaved. Caller retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multilinestring_zm( const struct tg_line *const lines[], int nlines, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multilinestring(lines, nlines); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_Z|HAS_M); } /// Creates a MultiPolygon geometry that includes Z coordinates. /// @param polys An array of polygons, caller retains ownership. /// @param npolys The number of polygons in array /// @param coords Array of doubles representing each Z coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multipolygon_z( const struct tg_poly *const polys[], int npolys, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multipolygon(polys, npolys); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_Z); } /// Creates a MultiPolygon geometry that includes M coordinates. /// @param polys An array of polygons, caller retains ownership. /// @param npolys The number of polygons in array /// @param coords Array of doubles representing each M coordinate, caller /// retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multipolygon_m( const struct tg_poly *const polys[], int npolys, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multipolygon(polys, npolys); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_M); } /// Creates a MultiPolygon geometry that includes ZM coordinates. /// @param polys An array of polygons, caller retains ownership. /// @param npolys The number of polygons in array /// @param coords Array of doubles representing each Z and M coordinate, /// interleaved. Caller retains ownership. /// @param ncoords Number of doubles in array. /// @return A newly allocated geometry. /// @return NULL if system is out of memory. /// @note The caller is responsible for freeing with tg_geom_free(). /// @see GeometryConstructorsEx struct tg_geom *tg_geom_new_multipolygon_zm( const struct tg_poly *const polys[], int npolys, const double *coords, int ncoords) { struct tg_geom *geom = tg_geom_new_multipolygon(polys, npolys); if (!geom) return NULL; return fill_extra_coords(geom, coords, ncoords, HAS_Z|HAS_M); } /// Clones a geometry /// @param geom Input geometry, caller retains ownership. /// @return A duplicate of the provided geometry. /// @note The caller is responsible for freeing with tg_geom_free(). /// @note This method of cloning uses implicit sharing through an atomic /// reference counter. /// @see GeometryConstructors struct tg_geom *tg_geom_clone(const struct tg_geom *geom) { if (!geom || getnoheap(geom)) { return tg_geom_copy(geom); } struct tg_geom *geom_mut = (struct tg_geom*)geom; rc_retain(&geom_mut->head.rc); return geom_mut; } static void geom_free(struct tg_geom *geom) { if (getnoheap(geom) || !rc_release(&geom->head.rc)) { return; } switch (gettype(geom)) { case TG_POINT: break; case TG_LINESTRING: tg_line_free(geom->line); break; case TG_POLYGON: tg_poly_free(geom->poly); break; case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { if (geom->multi->geoms) { for (int i = 0; i < geom->multi->ngeoms; i++) { tg_geom_free(geom->multi->geoms[i]); } tg_free(geom->multi->geoms); } if (geom->multi->index) { tg_free(geom->multi->index); } if (geom->multi->ixgeoms) { tg_free(geom->multi->ixgeoms); } tg_free(geom->multi); } break; } if (gettype(geom) != TG_POINT && geom->coords) { tg_free(geom->coords); } if (geom->error) { // error and xjson share the same memory, so this copy covers both. tg_free(geom->error); } tg_free(geom); } /// Releases the memory associated with a geometry. /// @param geom Input geometry /// @see GeometryConstructors void tg_geom_free(struct tg_geom *geom) { if (!geom) { return; } switch (getbase(geom)) { case BASE_GEOM: geom_free(geom); break; case BASE_POINT: boxed_point_free((struct boxed_point*)geom); break; case BASE_LINE: tg_line_free((struct tg_line*)geom); break; case BASE_RING: tg_ring_free((struct tg_ring*)geom); break; case BASE_POLY: tg_poly_free((struct tg_poly*)geom); break; } } /// Returns the geometry type. e.g. TG_POINT, TG_POLYGON, TG_LINESTRING /// @param geom Input geometry /// @return The geometry type /// @see [tg_geom_type](.#tg_geom_type) /// @see tg_geom_type_string() /// @see GeometryAccessors enum tg_geom_type tg_geom_typeof(const struct tg_geom *geom) { if (!geom) return 0; return gettype(geom); } /// Returns true if the geometry is a GeoJSON Feature. /// @param geom Input geometry /// @return True or false /// @see GeometryAccessors bool tg_geom_is_feature(const struct tg_geom *geom) { return geom && (getflags(geom)&IS_FEATURE) == IS_FEATURE; } /// Returns true if the geometry is a GeoJSON FeatureCollection. /// @param geom Input geometry /// @return True or false /// @see GeometryAccessors bool tg_geom_is_featurecollection(const struct tg_geom *geom) { return geom && (getflags(geom)&IS_FEATURE_COL) == IS_FEATURE_COL; } static struct tg_rect geom_rect(const struct tg_geom *geom) { struct tg_rect rect = { 0 }; switch (gettype(geom)) { case TG_POINT: return tg_point_rect(geom->point); case TG_LINESTRING: return tg_line_rect(geom->line); case TG_POLYGON: return tg_poly_rect(geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { rect = geom->multi->rect; } } return rect; } /// Returns the minimum bounding rectangle of a geometry. /// @param geom Input geometry /// @return Minimum bounding rectangle /// @see tg_rect /// @see GeometryAccessors struct tg_rect tg_geom_rect(const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return geom_rect(geom); case BASE_POINT: return tg_point_rect(((struct boxed_point*)geom)->point); case BASE_LINE: return tg_line_rect((struct tg_line*)geom); case BASE_RING: return tg_ring_rect((struct tg_ring*)geom); case BASE_POLY: return tg_poly_rect((struct tg_poly*)geom); } } return (struct tg_rect) { 0 }; } /// Returns the underlying point for the provided geometry. /// @param geom Input geometry /// @return For a TG_POINT geometry, returns the point. /// @return For everything else returns the center of the geometry's bounding /// rectangle. /// @see tg_point /// @see GeometryAccessors struct tg_point tg_geom_point(const struct tg_geom *geom) { if (!geom) return (struct tg_point) { 0 }; if (getbase(geom) == BASE_POINT) { return ((struct boxed_point*)geom)->point; } if (getbase(geom) == BASE_GEOM && gettype(geom) == TG_POINT) { return geom->point; } struct tg_rect rect = tg_geom_rect(geom); return (struct tg_point) { (rect.min.x + rect.max.x) / 2, (rect.min.y + rect.max.y) / 2, }; } static size_t geom_memsize(const struct tg_geom *geom) { size_t size = sizeof(struct tg_geom); switch (gettype(geom)) { case TG_POINT: break; case TG_LINESTRING: size += tg_line_memsize(geom->line); break; case TG_POLYGON: size += tg_poly_memsize(geom->poly); break; case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { size += sizeof(struct multi); size += geom->multi->ngeoms*sizeof(struct tg_geom*); for (int i = 0; i < geom->multi->ngeoms; i++) { size += tg_geom_memsize(geom->multi->geoms[i]); } if (geom->multi->index) { size += geom->multi->index->memsz; } if (geom->multi->ixgeoms) { size += geom->multi->ngeoms*sizeof(int); } } break; } if (gettype(geom) != TG_POINT && geom->coords) { size += geom->ncoords*sizeof(double); } if (geom->xjson) { // geom->error shares the same memory stored as a C string. size += strlen(geom->xjson)+1; } return size; } /// Returns the allocation size of the geometry. /// @param geom Input geometry /// @return Size of geometry in bytes size_t tg_geom_memsize(const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return geom_memsize(geom); case BASE_POINT: return sizeof(struct boxed_point); case BASE_LINE: return tg_line_memsize((struct tg_line*)geom); case BASE_RING: return tg_ring_memsize((struct tg_ring*)geom); case BASE_POLY: return tg_poly_memsize((struct tg_poly*)geom); } } return 0; } /// Returns the underlying line for the provided geometry. /// @param geom Input geometry /// @return For a TG_LINESTRING geometry, returns the line. /// @return For everything else returns NULL. /// @see tg_line /// @see GeometryAccessors const struct tg_line *tg_geom_line(const struct tg_geom *geom) { if (!geom) return NULL; if (getbase(geom) == BASE_LINE) return (struct tg_line*)geom; if (getbase(geom) == BASE_GEOM && gettype(geom) == TG_LINESTRING) { return geom->line; } return NULL; } /// Returns the underlying polygon for the provided geometry. /// @param geom Input geometry /// @return For a TG_POLYGON geometry, returns the polygon. /// @return For everything else returns NULL. /// @see tg_poly /// @see GeometryAccessors const struct tg_poly *tg_geom_poly(const struct tg_geom *geom) { if (!geom) return NULL; if (getbase(geom) == BASE_RING) return (struct tg_poly*)geom; if (getbase(geom) == BASE_POLY) return (struct tg_poly*)geom; if (getbase(geom) == BASE_GEOM && gettype(geom) == TG_POLYGON) { return geom->poly; } return NULL; } /// Returns the number of points in a MultiPoint geometry. /// @param geom Input geometry /// @return For a TG_MULTIPOINT geometry, returns the number of points. /// @return For everything else returns zero. /// @see tg_geom_point_at() /// @see GeometryAccessors int tg_geom_num_points(const struct tg_geom *geom) { if (!geom) return 0; if (getbase(geom) == BASE_GEOM && gettype(geom) == TG_MULTIPOINT && geom->multi) { return geom->multi->ngeoms; } return 0; } /// Returns the number of lines in a MultiLineString geometry. /// @param geom Input geometry /// @return For a TG_MULTILINESTRING geometry, returns the number of lines. /// @return For everything else returns zero. /// @see tg_geom_line_at() /// @see GeometryAccessors int tg_geom_num_lines(const struct tg_geom *geom) { if (!geom) return 0; if (getbase(geom) == BASE_GEOM && gettype(geom) == TG_MULTILINESTRING && geom->multi) { return geom->multi->ngeoms; } return 0; } /// Returns the number of polygons in a MultiPolygon geometry. /// @param geom Input geometry /// @return For a TG_MULTIPOLYGON geometry, returns the number of polygons. /// @return For everything else returns zero. /// @see tg_geom_poly_at() /// @see GeometryAccessors int tg_geom_num_polys(const struct tg_geom *geom) { if (!geom) return 0; if (getbase(geom) == BASE_GEOM && gettype(geom) == TG_MULTIPOLYGON && geom->multi) { return geom->multi->ngeoms; } return 0; } /// Returns the number of geometries in a GeometryCollection geometry. /// @param geom Input geometry /// @return For a TG_MULTIGEOMETRY geometry, returns the number of geometries. /// @return For everything else returns zero. /// @note A geometry that is a GeoJSON FeatureCollection can use this function /// to get number features in its collection. /// @see tg_geom_geometry_at() /// @see tg_geom_is_featurecollection() /// @see GeometryAccessors int tg_geom_num_geometries(const struct tg_geom *geom) { if (!geom) return 0; if (getbase(geom) == BASE_GEOM && gettype(geom) == TG_GEOMETRYCOLLECTION && geom->multi) { return geom->multi->ngeoms; } return 0; } /// Returns the point at index for a MultiPoint geometry. /// @param geom Input geometry /// @param index Index of point /// @return The point at index. Returns an empty point if the geometry type is /// not TG_MULTIPOINT or when the provided index is out of range. /// @see tg_geom_num_points() /// @see GeometryAccessors struct tg_point tg_geom_point_at(const struct tg_geom *geom, int index) { if (geom && getbase(geom) == BASE_GEOM && gettype(geom) == TG_MULTIPOINT && geom->multi &&index >= 0 && index <= geom->multi->ngeoms) { return ((struct boxed_point*)geom->multi->geoms[index])->point; } return (struct tg_point) { 0 }; } /// Returns the line at index for a MultiLineString geometry. /// @param geom Input geometry /// @param index Index of line /// @return The line at index. Returns NULL if the geometry type is not /// TG_MULTILINE or when the provided index is out of range. /// @see tg_geom_num_lines() /// @see GeometryAccessors const struct tg_line *tg_geom_line_at(const struct tg_geom *geom, int index) { if (geom && getbase(geom) == BASE_GEOM && gettype(geom) == TG_MULTILINESTRING && geom->multi &&index >= 0 && index <= geom->multi->ngeoms) { return (struct tg_line*)geom->multi->geoms[index]; } return NULL; } /// Returns the polygon at index for a MultiPolygon geometry. /// @param geom Input geometry /// @param index Index of polygon /// @return The polygon at index. Returns NULL if the geometry type is not /// TG_MULTIPOLYGON or when the provided index is out of range. /// @see tg_geom_num_polys() /// @see GeometryAccessors const struct tg_poly *tg_geom_poly_at(const struct tg_geom *geom, int index) { if (geom && getbase(geom) == BASE_GEOM && gettype(geom) == TG_MULTIPOLYGON && geom->multi && index >= 0 && index <= geom->multi->ngeoms) { return (struct tg_poly *)geom->multi->geoms[index]; } return NULL; } /// Returns the geometry at index for a GeometryCollection geometry. /// @param geom Input geometry /// @param index Index of geometry /// @return For a TG_MULTIGEOMETRY geometry, returns the number of geometries. /// @return For everything else returns zero. /// @note A geometry that is a GeoJSON FeatureCollection can use this /// function to get number features in its collection. /// @see tg_geom_geometry_at() /// @see tg_geom_is_featurecollection() /// @see GeometryAccessors const struct tg_geom *tg_geom_geometry_at(const struct tg_geom *geom, int index) { if (geom && getbase(geom) == BASE_GEOM && gettype(geom) == TG_GEOMETRYCOLLECTION && geom->multi && index >= 0 && index <= geom->multi->ngeoms) { return geom->multi->geoms[index]; } return NULL; } static bool geom_foreach(const struct tg_geom *geom, bool(*iter)(const struct tg_geom *geom, void *udata), void *udata) { if (!geom) { return true; } if (getbase(geom) == BASE_GEOM) { switch (gettype(geom)) { case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { if (!iter(geom->multi->geoms[i], udata)) return false; } } return true; default: break; } } return iter(geom, udata); } /// tg_geom_foreach flattens a Multi* or GeometryCollection, iterating over /// every child (or child of child in case of nested geometry collection). /// If the provided geometry is not a Multi* or GeometryCollection then only /// that geometry is returned by the iter. /// Empty geometries are not returned. void tg_geom_foreach(const struct tg_geom *geom, bool(*iter)(const struct tg_geom *geom, void *udata), void *udata) { geom_foreach(geom, iter, udata); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ///// Intersects //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// static bool point_intersects_geom(struct tg_point point, const struct tg_geom *geom); static bool point_intersects_base_geom(struct tg_point point, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_point_intersects_point(point, geom->point); case TG_LINESTRING: return tg_point_intersects_line(point, geom->line); case TG_POLYGON: return tg_point_intersects_poly(point, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { if (point_intersects_geom(point, geom->multi->geoms[i])) { return true; } } } return false; } } return false; } static bool point_intersects_geom(struct tg_point point, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return point_intersects_base_geom(point, geom); case BASE_POINT: return tg_point_intersects_point(point, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_point_intersects_line(point, ((struct tg_line*)geom)); case BASE_RING: return tg_point_intersects_poly(point, ((struct tg_poly*)geom)); case BASE_POLY: return tg_point_intersects_poly(point, ((struct tg_poly*)geom)); } } return false; } static bool line_intersects_geom(struct tg_line *line, const struct tg_geom *geom); static bool line_intersects_base_geom(struct tg_line *line, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_line_intersects_point(line, geom->point); case TG_LINESTRING: return tg_line_intersects_line(line, geom->line); case TG_POLYGON: return tg_line_intersects_poly(line, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { if (line_intersects_geom(line, geom->multi->geoms[i])) { return true; } } } return false; } } return false; } static bool line_intersects_geom(struct tg_line *line, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return line_intersects_base_geom(line, geom); case BASE_POINT: return tg_line_intersects_point(line, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_line_intersects_line(line, ((struct tg_line*)geom)); case BASE_RING: return tg_line_intersects_poly(line, ((struct tg_poly*)geom)); case BASE_POLY: return tg_line_intersects_poly(line, ((struct tg_poly*)geom)); } } return false; } static bool poly_intersects_geom(struct tg_poly *poly, const struct tg_geom *geom); static bool poly_intersects_base_geom(struct tg_poly *poly, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_poly_intersects_point(poly, geom->point); case TG_LINESTRING: return tg_poly_intersects_line(poly, geom->line); case TG_POLYGON: return tg_poly_intersects_poly(poly, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { if (poly_intersects_geom(poly, geom->multi->geoms[i])) { return true; } } } return false; } } return false; } static bool poly_intersects_geom(struct tg_poly *poly, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return poly_intersects_base_geom(poly, geom); case BASE_POINT: return tg_poly_intersects_point(poly, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_poly_intersects_line(poly, ((struct tg_line*)geom)); case BASE_RING: return tg_poly_intersects_poly(poly, ((struct tg_poly*)geom)); case BASE_POLY: return tg_poly_intersects_poly(poly, ((struct tg_poly*)geom)); } } return false; } struct multiiterctx { bool isect; const struct tg_geom *other; }; static bool multiiter(const struct tg_geom *geom, int index, void *udata) { (void)index; struct multiiterctx *ctx = udata; if (tg_geom_intersects(geom, ctx->other)) { ctx->isect = true; return false; } return true; } static bool base_geom_intersects_geom(const struct tg_geom *geom, const struct tg_geom *other) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return point_intersects_geom(geom->point, other); case TG_LINESTRING: return line_intersects_geom(geom->line, other); case TG_POLYGON: return poly_intersects_geom(geom->poly, other); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: { struct multiiterctx ctx = { .other = other }; tg_geom_search(geom, tg_geom_rect(geom), multiiter, &ctx); return ctx.isect; }} } return false; } /// Tests whether two geometries intersect. /// @see GeometryPredicates bool tg_geom_intersects(const struct tg_geom *geom, const struct tg_geom *other) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return base_geom_intersects_geom(geom, other); case BASE_POINT: return point_intersects_geom(((struct boxed_point*)geom)->point, other); case BASE_LINE: return line_intersects_geom((struct tg_line*)geom, other); case BASE_RING: return poly_intersects_geom((struct tg_poly*)geom, other); case BASE_POLY: return poly_intersects_geom((struct tg_poly*)geom, other); } } return false; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ///// Covers //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// static bool point_covers_geom(struct tg_point point, const struct tg_geom *geom); static bool point_covers_base_geom(struct tg_point point, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_point_covers_point(point, geom->point); case TG_LINESTRING: return tg_point_covers_line(point, geom->line); case TG_POLYGON: return tg_point_covers_poly(point, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (!geom->multi || geom->multi->ngeoms == 0) return false; for (int i = 0; i < geom->multi->ngeoms; i++) { if (!point_covers_geom(point, geom->multi->geoms[i])) { return false; } } return true; } } return false; } static bool point_covers_geom(struct tg_point point, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return point_covers_base_geom(point, geom); case BASE_POINT: return tg_point_covers_point(point, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_point_covers_line(point, ((struct tg_line*)geom)); case BASE_RING: return tg_point_covers_poly(point, ((struct tg_poly*)geom)); case BASE_POLY: return tg_point_covers_poly(point, ((struct tg_poly*)geom)); } } return false; } static bool line_covers_geom(struct tg_line *line, const struct tg_geom *geom); static bool line_covers_base_geom(struct tg_line *line, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_line_covers_point(line, geom->point); case TG_LINESTRING: return tg_line_covers_line(line, geom->line); case TG_POLYGON: return tg_line_covers_poly(line, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (!geom->multi || geom->multi->ngeoms == 0) return false; for (int i = 0; i < geom->multi->ngeoms; i++) { if (!line_covers_geom(line, geom->multi->geoms[i])) { return false; } } return true; } } return false; } static bool line_covers_geom(struct tg_line *line, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return line_covers_base_geom(line, geom); case BASE_POINT: return tg_line_covers_point(line, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_line_covers_line(line, ((struct tg_line*)geom)); case BASE_RING: return tg_line_covers_poly(line, ((struct tg_poly*)geom)); case BASE_POLY: return tg_line_covers_poly(line, ((struct tg_poly*)geom)); } } return false; } static bool poly_covers_geom(struct tg_poly *poly, const struct tg_geom *geom); static bool poly_covers_base_geom(struct tg_poly *poly, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_poly_covers_point(poly, geom->point); case TG_LINESTRING: return tg_poly_covers_line(poly, geom->line); case TG_POLYGON: return tg_poly_covers_poly(poly, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (!geom->multi || geom->multi->ngeoms == 0) return false; for (int i = 0; i < geom->multi->ngeoms; i++) { if (!poly_covers_geom(poly, geom->multi->geoms[i])) { return false; } } return true; } } return false; } static bool poly_covers_geom(struct tg_poly *poly, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return poly_covers_base_geom(poly, geom); case BASE_POINT: return tg_poly_covers_point(poly, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_poly_covers_line(poly, ((struct tg_line*)geom)); case BASE_RING: return tg_poly_covers_poly(poly, ((struct tg_poly*)geom)); case BASE_POLY: return tg_poly_covers_poly(poly, ((struct tg_poly*)geom)); } } return false; } struct geom_covers_iter_ctx { const struct tg_geom *geom; bool result; }; static bool geom_covers_iter0(const struct tg_geom *geom, void *udata) { struct geom_covers_iter_ctx *ctx = udata; if (tg_geom_covers(geom, ctx->geom)) { // found a child object that contains geom, end inner loop ctx->result = true; return false; } return true; } bool tg_geom_is_empty(const struct tg_geom *geom); static bool base_geom_deep_empty(const struct tg_geom *geom) { switch (gettype(geom)) { case TG_POINT: return false; case TG_LINESTRING: return tg_line_empty(geom->line); case TG_POLYGON: return tg_poly_empty(geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { if (!tg_geom_is_empty(geom->multi->geoms[i])) { return false; } } } } return true; } /// Tests whether a geometry is empty. An empty geometry is one that has no /// interior boundary. /// @param geom Input geometry /// @return True or false bool tg_geom_is_empty(const struct tg_geom *geom) { if (geom) { if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) return true; switch (getbase(geom)) { case BASE_GEOM: return base_geom_deep_empty(geom); case BASE_POINT: return false; case BASE_LINE: return tg_line_empty((struct tg_line*)geom); case BASE_RING: return tg_poly_empty((struct tg_poly*)geom); case BASE_POLY: return tg_poly_empty((struct tg_poly*)geom); } } return true; } static bool geom_covers_iter(const struct tg_geom *geom, void *udata) { struct geom_covers_iter_ctx *ctx = udata; // skip empty geometries if (tg_geom_is_empty(geom)) { return true; } struct geom_covers_iter_ctx ctx0 = { .geom = geom }; tg_geom_foreach(ctx->geom, geom_covers_iter0, &ctx0); if (!ctx0.result) { // unmark and quit the loop ctx->result = false; return false; } // mark that at least one geom is contained ctx->result = true; return true; } static bool base_geom_covers_geom(const struct tg_geom *geom, const struct tg_geom *other) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return point_covers_geom(geom->point, other); case TG_LINESTRING: return line_covers_geom(geom->line, other); case TG_POLYGON: return poly_covers_geom(geom->poly, other); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: { // all children of 'other' must be fully within 'geom' struct geom_covers_iter_ctx ctx = { .geom = geom }; tg_geom_foreach(other, geom_covers_iter, &ctx); return ctx.result; } } } return false; } /// Tests whether a geometry 'a' fully contains geometry 'b'. /// @see GeometryPredicates bool tg_geom_covers(const struct tg_geom *geom, const struct tg_geom *other) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return base_geom_covers_geom(geom, other); case BASE_POINT: return point_covers_geom(((struct boxed_point*)geom)->point, other); case BASE_LINE: return line_covers_geom((struct tg_line*)geom, other); case BASE_RING: return poly_covers_geom((struct tg_poly*)geom, other); case BASE_POLY: return poly_covers_geom((struct tg_poly*)geom, other); } } return false; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ///// Contains //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// static bool poly_contains_geom(struct tg_poly *poly, const struct tg_geom *geom); static bool poly_contains_base_geom(struct tg_poly *poly, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_poly_contains_point(poly, geom->point); case TG_LINESTRING: return tg_poly_contains_line(poly, geom->line); case TG_POLYGON: return tg_poly_contains_poly(poly, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: { bool contains = false; if (geom->multi && geom->multi->ngeoms > 0) { contains = true; for (int i = 0; i < geom->multi->ngeoms; i++) { if (!poly_contains_geom(poly, geom->multi->geoms[i])) { contains = false; break; } } } return contains; }} } return false; } static bool poly_contains_geom(struct tg_poly *poly, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return poly_contains_base_geom(poly, geom); case BASE_POINT: return tg_poly_contains_point(poly, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_poly_contains_line(poly, ((struct tg_line*)geom)); case BASE_RING: return tg_poly_contains_poly(poly, ((struct tg_poly*)geom)); case BASE_POLY: return tg_poly_contains_poly(poly, ((struct tg_poly*)geom)); } } return false; } static bool line_contains_geom(struct tg_line *line, const struct tg_geom *geom); static bool line_contains_base_geom(struct tg_line *line, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_line_contains_point(line, geom->point); case TG_LINESTRING: return tg_line_contains_line(line, geom->line); case TG_POLYGON: return tg_line_contains_poly(line, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: { bool contains = false; if (geom->multi && geom->multi->ngeoms > 0) { contains = true; for (int i = 0; i < geom->multi->ngeoms; i++) { if (!line_contains_geom(line, geom->multi->geoms[i])) { contains = false; break; } } } return contains; }} } return false; } static bool line_contains_geom(struct tg_line *line, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return line_contains_base_geom(line, geom); case BASE_POINT: return tg_line_contains_point(line, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_line_contains_line(line, ((struct tg_line*)geom)); case BASE_RING: return tg_line_contains_poly(line, ((struct tg_poly*)geom)); case BASE_POLY: return tg_line_contains_poly(line, ((struct tg_poly*)geom)); } } return false; } static bool point_contains_geom(struct tg_point point, const struct tg_geom *geom); static bool point_contains_base_geom(struct tg_point point, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_point_contains_point(point, geom->point); case TG_LINESTRING: return tg_point_contains_line(point, geom->line); case TG_POLYGON: return tg_point_contains_poly(point, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: { bool contains = false; if (geom->multi && geom->multi->ngeoms > 0) { contains = true; for (int i = 0; i < geom->multi->ngeoms; i++) { if (!point_contains_geom(point, geom->multi->geoms[i])) { contains = false; break; } } } return contains; }} } return false; } struct geom_contains_iter_ctx { const struct tg_geom *geom; bool result; }; static bool geom_contains_iter0(const struct tg_geom *geom, void *udata) { struct geom_contains_iter_ctx *ctx = udata; if (tg_geom_contains(geom, ctx->geom)) { // found a child object that contains geom, end inner loop ctx->result = true; return false; } return true; } static bool geom_contains_iter(const struct tg_geom *geom, void *udata) { struct geom_contains_iter_ctx *ctx = udata; // skip empty geometries if (!tg_geom_is_empty(geom)) { struct geom_contains_iter_ctx ctx0 = { .geom = geom }; tg_geom_foreach(ctx->geom, geom_contains_iter0, &ctx0); if (!ctx0.result) { // unmark and quit the loop ctx->result = false; return false; } // mark that at least one geom is contained ctx->result = true; } return true; } static bool base_geom_contains_geom(const struct tg_geom *geom, const struct tg_geom *other) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return point_contains_geom(geom->point, other); case TG_LINESTRING: return line_contains_geom(geom->line, other); case TG_POLYGON: return poly_contains_geom(geom->poly, other); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: { // all children of 'other' must be fully within 'geom' struct geom_contains_iter_ctx ctx = { .geom = geom }; tg_geom_foreach(other, geom_contains_iter, &ctx); return ctx.result; } } } return false; } static bool point_contains_geom(struct tg_point point, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return point_contains_base_geom(point, geom); case BASE_POINT: return tg_point_contains_point(point, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_point_contains_line(point, ((struct tg_line*)geom)); case BASE_RING: return tg_point_contains_poly(point, ((struct tg_poly*)geom)); case BASE_POLY: return tg_point_contains_poly(point, ((struct tg_poly*)geom)); } } return false; } /// Tests whether 'a' contains 'b', and 'b' is not touching the boundary of 'a'. /// @note Works the same as `tg_geom_within(b, a)` /// @warning This predicate returns **false** when geometry 'b' is *on* or /// *touching* the boundary of geometry 'a'. Such as when a point is on the /// edge of a polygon. /// For full coverage, consider using @ref tg_geom_covers. /// @see GeometryPredicates bool tg_geom_contains(const struct tg_geom *geom, const struct tg_geom *other) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return base_geom_contains_geom(geom, other); case BASE_POINT: return point_contains_geom(((struct boxed_point*)geom)->point, other); case BASE_LINE: return line_contains_geom((struct tg_line*)geom, other); case BASE_RING: return poly_contains_geom((struct tg_poly*)geom, other); case BASE_POLY: return poly_contains_geom((struct tg_poly*)geom, other); } } return false; } bool tg_poly_contains_geom(struct tg_poly *a, const struct tg_geom *b) { return poly_contains_geom(a, b); } bool tg_line_contains_geom(struct tg_line *a, const struct tg_geom *b) { return line_contains_geom(a, b); } bool tg_point_contains_geom(struct tg_point a, const struct tg_geom *b) { return point_contains_geom(a, b); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ///// Touches //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// static bool point_touches_geom(struct tg_point point, const struct tg_geom *geom); static bool point_touches_base_geom(struct tg_point point, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_point_touches_point(point, geom->point); case TG_LINESTRING: return tg_point_touches_line(point, geom->line); case TG_POLYGON: return tg_point_touches_poly(point, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { if (point_touches_geom(point, geom->multi->geoms[i])) { return true; } } } return false; } } return false; } static bool point_touches_geom(struct tg_point point, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return point_touches_base_geom(point, geom); case BASE_POINT: return tg_point_touches_point(point, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_point_touches_line(point, ((struct tg_line*)geom)); case BASE_RING: return tg_point_touches_poly(point, ((struct tg_poly*)geom)); case BASE_POLY: return tg_point_touches_poly(point, ((struct tg_poly*)geom)); } } return false; } static bool line_touches_geom(struct tg_line *line, const struct tg_geom *geom); static bool line_touches_base_geom(struct tg_line *line, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_line_touches_point(line, geom->point); case TG_LINESTRING: return tg_line_touches_line(line, geom->line); case TG_POLYGON: return tg_line_touches_poly(line, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { if (line_touches_geom(line, geom->multi->geoms[i])) { return true; } } } return false; } } return false; } static bool line_touches_geom(struct tg_line *line, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return line_touches_base_geom(line, geom); case BASE_POINT: return tg_line_touches_point(line, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_line_touches_line(line, ((struct tg_line*)geom)); case BASE_RING: return tg_line_touches_poly(line, ((struct tg_poly*)geom)); case BASE_POLY: return tg_line_touches_poly(line, ((struct tg_poly*)geom)); } } return false; } static bool poly_touches_geom(struct tg_poly *poly, const struct tg_geom *geom); static bool poly_touches_base_geom(struct tg_poly *poly, const struct tg_geom *geom) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return tg_poly_touches_point(poly, geom->point); case TG_LINESTRING: return tg_poly_touches_line(poly, geom->line); case TG_POLYGON: return tg_poly_touches_poly(poly, geom->poly); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: { bool touches = false; if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_geom *child = geom->multi->geoms[i]; if (poly_touches_geom(poly, child)) { touches = true; } else if (poly_intersects_geom(poly, child)) { return false; } } } return touches; }} } return false; } static bool poly_touches_geom(struct tg_poly *poly, const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return poly_touches_base_geom(poly, geom); case BASE_POINT: return tg_poly_touches_point(poly, ((struct boxed_point*)geom)->point); case BASE_LINE: return tg_poly_touches_line(poly, ((struct tg_line*)geom)); case BASE_RING: return tg_poly_touches_poly(poly, ((struct tg_poly*)geom)); case BASE_POLY: return tg_poly_touches_poly(poly, ((struct tg_poly*)geom)); } } return false; } static bool base_geom_touches_geom(const struct tg_geom *geom, const struct tg_geom *other) { if ((getflags(geom)&IS_EMPTY) != IS_EMPTY) { switch (gettype(geom)) { case TG_POINT: return point_touches_geom(geom->point, other); case TG_LINESTRING: return line_touches_geom(geom->line, other); case TG_POLYGON: return poly_touches_geom(geom->poly, other); case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: { bool touches = false; if (geom->multi) { for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_geom *child = geom->multi->geoms[i]; if (tg_geom_touches(child, other)) { touches = true; } else if (tg_geom_intersects(child, other)) { return false; } } } return touches; }} } return false; } /// Tests whether a geometry 'a' touches 'b'. /// They have at least one point in common, but their interiors do not /// intersect. /// @see GeometryPredicates bool tg_geom_touches(const struct tg_geom *geom, const struct tg_geom *other) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return base_geom_touches_geom(geom, other); case BASE_POINT: return point_touches_geom(((struct boxed_point*)geom)->point, other); case BASE_LINE: return line_touches_geom((struct tg_line*)geom, other); case BASE_RING: return poly_touches_geom((struct tg_poly*)geom, other); case BASE_POLY: return poly_touches_geom((struct tg_poly*)geom, other); } } return false; } bool tg_poly_touches_geom(struct tg_poly *a, const struct tg_geom *b) { return poly_touches_geom(a, b); } bool tg_line_touches_geom(struct tg_line *a, const struct tg_geom *b) { return line_touches_geom(a, b); } bool tg_point_touches_geom(struct tg_point a, const struct tg_geom *b) { return point_touches_geom(a, b); } /// Tests whether a geometry 'a' fully contains point 'b'. /// @see GeometryPredicates bool tg_geom_covers_point(const struct tg_geom *a, struct tg_point b) { struct boxed_point bpoint = { .point = b, }; setbase(&bpoint, BASE_POINT); settype(&bpoint, TG_POINT); return tg_geom_covers(a, (struct tg_geom*)&bpoint); } /// Tests whether a geometry fully contains a point using xy coordinates. /// @see GeometryPredicates bool tg_geom_covers_xy(const struct tg_geom *a, double x, double y) { return tg_geom_covers_point(a, (struct tg_point){ .x = x, .y = y }); } /// Tests whether a geometry 'a' intersects point 'b'. /// @see GeometryPredicates bool tg_geom_intersects_point(const struct tg_geom *a, struct tg_point b) { struct boxed_point bpoint = { .point = b, }; setbase(&bpoint, BASE_POINT); settype(&bpoint, TG_POINT); return tg_geom_intersects(a, (struct tg_geom*)&bpoint); } /// Tests whether a geometry intersects a point using xy coordinates. /// @see GeometryPredicates bool tg_geom_intersects_xy(const struct tg_geom *a, double x, double y) { return tg_geom_intersects_point(a, (struct tg_point){ .x = x, .y = y }); } /// Get the extra coordinates for a geometry. /// @param geom Input geometry /// @return Array of coordinates /// @return NULL if there are no extra coordinates /// @note These are the raw coordinates provided by a constructor like /// tg_geom_new_polygon_z() or from a parsed source like WKT "POLYGON Z ...". /// @see tg_geom_num_extra_coords() const double *tg_geom_extra_coords(const struct tg_geom *geom) { if (!geom || getbase(geom) != BASE_GEOM || gettype(geom) == TG_POINT) { return NULL; } return geom->coords; } /// Get the number of extra coordinates for a geometry /// @param geom Input geometry /// @return The number of extra coordinates, or zero if none. /// @see tg_geom_extra_coords() int tg_geom_num_extra_coords(const struct tg_geom *geom) { if (!geom || getbase(geom) != BASE_GEOM || gettype(geom) == TG_POINT) { return 0; } return geom->ncoords; } /// Get the number of dimensions for a geometry. /// @param geom Input geometry /// @return 2 for standard geometries /// @return 3 when geometry has Z or M coordinates /// @return 4 when geometry has Z and M coordinates /// @return 0 when input is NULL int tg_geom_dims(const struct tg_geom *geom) { if (!geom) return 0; int dims = 2; if ((getflags(geom)&HAS_Z) == HAS_Z) dims++; if ((getflags(geom)&HAS_M) == HAS_M) dims++; return dims; } /// Tests whether a geometry has Z coordinates. /// @param geom Input geometry /// @return True or false bool tg_geom_has_z(const struct tg_geom *geom) { return (geom && (getflags(geom)&HAS_Z) == HAS_Z); } /// Tests whether a geometry has M coordinates. /// @param geom Input geometry /// @return True or false bool tg_geom_has_m(const struct tg_geom *geom) { return (geom && (getflags(geom)&HAS_M) == HAS_M); } /// Get the Z coordinate of a Point geometry. /// @param geom Input geometry /// @return For a TG_POINT geometry, returns the Z coordinate. /// @return For everything else returns zero. double tg_geom_z(const struct tg_geom *geom) { if (!geom || getbase(geom) != BASE_GEOM || gettype(geom) != TG_POINT) { return 0; } return geom->z; } /// Get the M coordinate of a Point geometry. /// @param geom Input geometry /// @return For a TG_POINT geometry, returns the M coordinate. /// @return For everything else returns zero. double tg_geom_m(const struct tg_geom *geom) { if (!geom || getbase(geom) != BASE_GEOM || gettype(geom) != TG_POINT) { return 0; } return geom->m; } /// Returns the indexing spread for a ring. /// /// The "spread" is the number of segments or rectangles that are grouped /// together to produce a unioned rectangle that is stored at a higher level. /// /// For a tree based structure, this would be the number of items per node. /// /// @param ring Input ring /// @return The spread, default is 16 /// @return Zero if ring has no indexing /// @see tg_ring_index_num_levels() /// @see tg_ring_index_level_num_rects() /// @see tg_ring_index_level_rect() /// @see RingFuncs int tg_ring_index_spread(const struct tg_ring *ring) { struct index *index = ring ? ring->index : NULL; return index_spread(index); } /// Returns the number of levels. /// @param ring Input ring /// @return The number of levels /// @return Zero if ring has no indexing /// @see tg_ring_index_spread() /// @see tg_ring_index_level_num_rects() /// @see tg_ring_index_level_rect() /// @see RingFuncs int tg_ring_index_num_levels(const struct tg_ring *ring) { struct index *index = ring ? ring->index : NULL; return index_num_levels(index); } /// Returns the number of rectangles at level. /// @param ring Input ring /// @param levelidx The index of level /// @return The number of index levels /// @return Zero if ring has no indexing or levelidx is out of bounds. /// @see tg_ring_index_spread() /// @see tg_ring_index_num_levels() /// @see tg_ring_index_level_rect() /// @see RingFuncs int tg_ring_index_level_num_rects(const struct tg_ring *ring, int levelidx) { struct index *index = ring ? ring->index : NULL; return index_level_num_rects(index, levelidx); } /// Returns a specific level rectangle. /// @param ring Input ring /// @param levelidx The index of level /// @param rectidx The index of rectangle /// @return The rectangle /// @return Empty rectangle if ring has no indexing, or levelidx or rectidx /// is out of bounds. /// @see tg_ring_index_spread() /// @see tg_ring_index_num_levels() /// @see tg_ring_index_level_num_rects() /// @see RingFuncs struct tg_rect tg_ring_index_level_rect(const struct tg_ring *ring, int levelidx, int rectidx) { struct index *index = ring ? ring->index : NULL; return index_level_rect(index, levelidx, rectidx); } /// Get the string representation of a geometry type. /// e.g. "Point", "Polygon", "LineString". /// @param type Input geometry type /// @return A string representing the type /// @note The returned string does not need to be freed. /// @see tg_geom_typeof() /// @see GeometryAccessors const char *tg_geom_type_string(enum tg_geom_type type) { switch (type) { case TG_POINT: return "Point"; case TG_LINESTRING: return "LineString"; case TG_POLYGON: return "Polygon"; case TG_MULTIPOINT: return "MultiPoint"; case TG_MULTILINESTRING: return "MultiLineString"; case TG_MULTIPOLYGON: return "MultiPolygon"; case TG_GEOMETRYCOLLECTION: return "GeometryCollection"; default: return "Unknown"; } } ///////////////////////////////////////////// // Formats -- GeoJSON, WKT, WKB, HEX ///////////////////////////////////////////// // maximum depth for all recursive input formats such as wkt, wkb, and json. #define MAXDEPTH 1024 #ifndef JSON_MAXDEPTH #define JSON_MAXDEPTH MAXDEPTH #endif #ifdef TG_NOAMALGA #include "deps/json.h" #else #define JSON_STATIC #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif // BEGIN json.c // https://github.com/tidwall/json.c // // Copyright 2023 Joshua J Baker. All rights reserved. // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. #include #include #include #ifndef JSON_STATIC #include "json.h" #else enum json_type { JSON_NULL, JSON_FALSE, JSON_NUMBER, JSON_STRING, JSON_TRUE, JSON_ARRAY, JSON_OBJECT, }; struct json { void *priv[4]; }; struct json_valid { bool valid; size_t pos; }; #define JSON_EXTERN static #endif #ifndef JSON_EXTERN #define JSON_EXTERN #endif #ifndef JSON_MAXDEPTH #define JSON_MAXDEPTH 1024 #endif struct vutf8res { int n; uint32_t cp; }; // parse and validate a single utf8 codepoint. // The first byte has already been checked from the vstring function. static inline struct vutf8res vutf8(const uint8_t data[], int64_t len) { uint32_t cp; int n = 0; if (data[0]>>4 == 14) { if (len < 3) goto fail; if (((data[1]>>6)|(data[2]>>6<<2)) != 10) goto fail; cp = ((uint32_t)(data[0]&15)<<12)|((uint32_t)(data[1]&63)<<6)| ((uint32_t)(data[2]&63)); n = 3; } else if (data[0]>>3 == 30) { if (len < 4) goto fail; if (((data[1]>>6)|(data[2]>>6<<2)|(data[3]>>6<<4)) != 42) goto fail; cp = ((uint32_t)(data[0]&7)<<18)|((uint32_t)(data[1]&63)<<12)| ((uint32_t)(data[2]&63)<<6)|((uint32_t)(data[3]&63)); n = 4; } else if (data[0]>>5 == 6) { if (len < 2) goto fail; if (data[1]>>6 != 2) goto fail; cp = ((uint32_t)(data[0]&31)<<6)|((uint32_t)(data[1]&63)); n = 2; } else { goto fail; } if (cp < 128) goto fail; // don't allow multibyte ascii characters if (cp >= 0x10FFFF) goto fail; // restricted to utf-16 if (cp >= 0xD800 && cp <= 0xDFFF) goto fail; // needs surrogate pairs return (struct vutf8res) { .n = n, .cp = cp }; fail: return (struct vutf8res) { 0 }; } static inline int64_t vesc(const uint8_t *json, int64_t jlen, int64_t i) { // The first byte has already been checked from the vstring function. i += 1; if (i == jlen) return -(i+1); switch (json[i]) { case '"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't': return i+1; case 'u': for (int j = 0; j < 4; j++) { i++; if (i == jlen) return -(i+1); if (!((json[i] >= '0' && json[i] <= '9') || (json[i] >= 'a' && json[i] <= 'f') || (json[i] >= 'A' && json[i] <= 'F'))) return -(i+1); } return i+1; } return -(i+1); } #ifndef ludo #define ludo #define ludo1(i,f) f; i++; #define ludo2(i,f) ludo1(i,f); ludo1(i,f); #define ludo4(i,f) ludo2(i,f); ludo2(i,f); #define ludo8(i,f) ludo4(i,f); ludo4(i,f); #define ludo16(i,f) ludo8(i,f); ludo8(i,f); #define ludo32(i,f) ludo16(i,f); ludo16(i,f); #define ludo64(i,f) ludo32(i,f); ludo32(i,f); #define for1(i,n,f) while(i+1<=(n)) { ludo1(i,f); } #define for2(i,n,f) while(i+2<=(n)) { ludo2(i,f); } for1(i,n,f); #define for4(i,n,f) while(i+4<=(n)) { ludo4(i,f); } for1(i,n,f); #define for8(i,n,f) while(i+8<=(n)) { ludo8(i,f); } for1(i,n,f); #define for16(i,n,f) while(i+16<=(n)) { ludo16(i,f); } for1(i,n,f); #define for32(i,n,f) while(i+32<=(n)) { ludo32(i,f); } for1(i,n,f); #define for64(i,n,f) while(i+64<=(n)) { ludo64(i,f); } for1(i,n,f); #endif static const uint8_t strtoksu[256] = { 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, #ifndef JSON_NOVALIDATEUTF8 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6, //0=ascii, 1=quote, 2=escape, 3=utf82, 4=utf83, 5=utf84, 6=error #endif }; static int64_t vstring(const uint8_t *json, int64_t jlen, int64_t i) { while (1) { for8(i, jlen, { if (strtoksu[json[i]]) goto tok; }) break; tok: if (json[i] == '"') { return i+1; #ifndef JSON_NOVALIDATEUTF8 } else if (json[i] > 127) { struct vutf8res res = vutf8(json+i, jlen-i); if (res.n == 0) break; i += res.n; #endif } else if (json[i] == '\\') { if ((i = vesc(json, jlen, i)) < 0) break; } else { break; } } return -(i+1); } static int64_t vnumber(const uint8_t *data, int64_t dlen, int64_t i) { i--; // sign if (data[i] == '-') { i++; if (i == dlen) return -(i+1); if (data[i] < '0' || data[i] > '9') return -(i+1); } // int if (data[i] == '0') { i++; } else { for (; i < dlen; i++) { if (data[i] >= '0' && data[i] <= '9') continue; break; } } // frac if (i == dlen) return i; if (data[i] == '.') { i++; if (i == dlen) return -(i+1); if (data[i] < '0' || data[i] > '9') return -(i+1); i++; for (; i < dlen; i++) { if (data[i] >= '0' && data[i] <= '9') continue; break; } } // exp if (i == dlen) return i; if (data[i] == 'e' || data[i] == 'E') { i++; if (i == dlen) return -(i+1); if (data[i] == '+' || data[i] == '-') i++; if (i == dlen) return -(i+1); if (data[i] < '0' || data[i] > '9') return -(i+1); i++; for (; i < dlen; i++) { if (data[i] >= '0' && data[i] <= '9') continue; break; } } return i; } static int64_t vnull(const uint8_t *data, int64_t dlen, int64_t i) { return i+3 <= dlen && data[i] == 'u' && data[i+1] == 'l' && data[i+2] == 'l' ? i+3 : -(i+1); } static int64_t vtrue(const uint8_t *data, int64_t dlen, int64_t i) { return i+3 <= dlen && data[i] == 'r' && data[i+1] == 'u' && data[i+2] == 'e' ? i+3 : -(i+1); } static int64_t vfalse(const uint8_t *data, int64_t dlen, int64_t i) { return i+4 <= dlen && data[i] == 'a' && data[i+1] == 'l' && data[i+2] == 's' && data[i+3] == 'e' ? i+4 : -(i+1); } static int64_t vcolon(const uint8_t *json, int64_t len, int64_t i) { if (i == len) return -(i+1); if (json[i] == ':') return i+1; do { switch (json[i]) { case ' ': case '\t': case '\n': case '\r': continue; case ':': return i+1; default: return -(i+1); } } while (++i < len); return -(i+1); } static int64_t vcomma(const uint8_t *json, int64_t len, int64_t i, uint8_t end) { if (i == len) return -(i+1); if (json[i] == ',') return i; do { switch (json[i]) { case ' ': case '\t': case '\n': case '\r': continue; case ',': return i; default: return json[i] == end ? i : -(i+1); } } while (++i < len); return -(i+1); } static int64_t vany(const uint8_t *data, int64_t dlen, int64_t i, int depth); static int64_t varray(const uint8_t *data, int64_t dlen, int64_t i, int depth) { for (; i < dlen; i++) { switch (data[i]) { case ' ': case '\t': case '\n': case '\r': continue; case ']': return i+1; default: for (; i < dlen; i++) { if ((i = vany(data, dlen, i, depth+1)) < 0) return i; if ((i = vcomma(data, dlen, i, ']')) < 0) return i; if (data[i] == ']') return i+1; } } } return -(i+1); } static int64_t vkey(const uint8_t *json, int64_t len, int64_t i) { for16(i, len, { if (strtoksu[json[i]]) goto tok; }) return -(i+1); tok: if (json[i] == '"') return i+1; return vstring(json, len, i); } static int64_t vobject(const uint8_t *data, int64_t dlen, int64_t i, int depth) { for (; i < dlen; i++) { switch (data[i]) { case '"': key: if ((i = vkey(data, dlen, i+1)) < 0) return i; if ((i = vcolon(data, dlen, i)) < 0) return i; if ((i = vany(data, dlen, i, depth+1)) < 0) return i; if ((i = vcomma(data, dlen, i, '}')) < 0) return i; if (data[i] == '}') return i+1; i++; for (; i < dlen; i++) { switch (data[i]) { case ' ': case '\t': case '\n': case '\r': continue; case '"': goto key; default: return -(i+1); } } return -(i+1); case ' ': case '\t': case '\n': case '\r': continue; case '}': return i+1; default: return -(i+1); } } return -(i+1); } static int64_t vany(const uint8_t *data, int64_t dlen, int64_t i, int depth) { if (depth > JSON_MAXDEPTH) return -(i+1); for (; i < dlen; i++) { switch (data[i]) { case ' ': case '\t': case '\n': case '\r': continue; case '{': return vobject(data, dlen, i+1, depth); case '[': return varray(data, dlen, i+1, depth); case '"': return vstring(data, dlen, i+1); case 't': return vtrue(data, dlen, i+1); case 'f': return vfalse(data, dlen, i+1); case 'n': return vnull(data, dlen, i+1); case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return vnumber(data, dlen, i+1); } break; } return -(i+1); } static int64_t vpayload(const uint8_t *data, int64_t dlen, int64_t i) { for (; i < dlen; i++) { switch (data[i]) { case ' ': case '\t': case '\n': case '\r': continue; default: if ((i = vany(data, dlen, i, 1)) < 0) return i; for (; i < dlen; i++) { switch (data[i]) { case ' ': case '\t': case '\n': case '\r': continue; default: return -(i+1); } } return i; } } return -(i+1); } JSON_EXTERN struct json_valid json_validn_ex(const char *json_str, size_t len, int opts) { (void)opts; // for future use int64_t ilen = len; if (ilen < 0) return (struct json_valid) { 0 }; int64_t pos = vpayload((uint8_t*)json_str, len, 0); if (pos > 0) return (struct json_valid) { .valid = true }; return (struct json_valid) { .pos = (-pos)-1 }; } JSON_EXTERN struct json_valid json_valid_ex(const char *json_str, int opts) { return json_validn_ex(json_str, json_str?strlen(json_str):0, opts); } JSON_EXTERN bool json_validn(const char *json_str, size_t len) { return json_validn_ex(json_str, len, 0).valid; } JSON_EXTERN bool json_valid(const char *json_str) { return json_validn(json_str, json_str?strlen(json_str):0); } // don't changes these flags without changing the numtoks table too. enum iflags { IESC = 1, IDOT = 2, ISCI = 4, ISIGN = 8 }; #define jmake(info, raw, end, len) ((struct json) { .priv = { \ (void*)(uintptr_t)(info), (void*)(uintptr_t)(raw), \ (void*)(uintptr_t)(end), (void*)(uintptr_t)(len) } }) #define jinfo(json) ((int)(uintptr_t)((json).priv[0])) #define jraw(json) ((uint8_t*)(uintptr_t)((json).priv[1])) #define jend(json) ((uint8_t*)(uintptr_t)((json).priv[2])) #define jlen(json) ((size_t)(uintptr_t)((json).priv[3])) static const uint8_t strtoksa[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, }; static inline size_t count_string(uint8_t *raw, uint8_t *end, int *infoout) { size_t len = end-raw; size_t i = 1; int info = 0; bool e = false; while (1) { for8(i, len, { if (strtoksa[raw[i]]) goto tok; e = false; }); break; tok: if (raw[i] == '"') { i++; if (!e) { break; } e = false; continue; } if (raw[i] == '\\') { info |= IESC; e = !e; } i++; } *infoout = info; return i; } static struct json take_string(uint8_t *raw, uint8_t *end) { int info = 0; size_t i = count_string(raw, end, &info); return jmake(info, raw, end, i); } static const uint8_t numtoks[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,1,0,1,3,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, 0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // don't changes these flags without changing enum iflags too. }; static struct json take_number(uint8_t *raw, uint8_t *end) { int64_t len = end-raw; int info = raw[0] == '-' ? ISIGN : 0; int64_t i = 1; for16(i, len, { if (!numtoks[raw[i]]) goto done; info |= (numtoks[raw[i]]-1); }); done: return jmake(info, raw, end, i); } static const uint8_t nesttoks[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,2,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,2,0,0, }; static size_t count_nested(uint8_t *raw, uint8_t *end) { size_t len = end-raw; size_t i = 1; int depth = 1; int kind = 0; if (i >= len) return i; while (depth) { for16(i, len, { if (nesttoks[raw[i]]) goto tok0; }); break; tok0: kind = nesttoks[raw[i]]; i++; if (kind-1) { depth += kind-3; } else { while (1) { for16(i, len, { if (raw[i]=='"') goto tok1; }); break; tok1: i++; if (raw[i-2] == '\\') { size_t j = i-3; size_t e = 1; while (j > 0 && raw[j] == '\\') { e = (e+1)&1; j--; } if (e) continue; } break; } } } return i; } static struct json take_literal(uint8_t *raw, uint8_t *end, size_t litlen) { size_t rlen = end-raw; return jmake(0, raw, end, rlen < litlen ? rlen : litlen); } static struct json peek_any(uint8_t *raw, uint8_t *end) { while (raw < end) { switch (*raw){ case '}': case ']': return (struct json){ 0 }; case '{': case '[': return jmake(0, raw, end, 0); case '"': return take_string(raw, end); case 'n': return take_literal(raw, end, 4); case 't': return take_literal(raw, end, 4); case 'f': return take_literal(raw, end, 5); case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return take_number(raw, end); } raw++; } return (struct json){ 0 }; } JSON_EXTERN struct json json_first(struct json json) { uint8_t *raw = jraw(json); uint8_t *end = jend(json); if (end <= raw || (*raw != '{' && *raw != '[')) return (struct json){0}; return peek_any(raw+1, end); } JSON_EXTERN struct json json_next(struct json json) { uint8_t *raw = jraw(json); uint8_t *end = jend(json); if (end <= raw) return (struct json){ 0 }; raw += jlen(json) == 0 ? count_nested(raw, end): jlen(json); return peek_any(raw, end); } JSON_EXTERN struct json json_parsen(const char *json_str, size_t len) { if (len > 0 && (json_str[0] == '[' || json_str[0] == '{')) { return jmake(0, json_str, json_str+len, 0); } if (len == 0) return (struct json){ 0 }; return peek_any((uint8_t*)json_str, (uint8_t*)json_str+len); } JSON_EXTERN struct json json_parse(const char *json_str) { return json_parsen(json_str, json_str?strlen(json_str):0); } JSON_EXTERN bool json_exists(struct json json) { return jraw(json) && jend(json); } JSON_EXTERN const char *json_raw(struct json json) { return (char*)jraw(json); } JSON_EXTERN size_t json_raw_length(struct json json) { if (jlen(json)) return jlen(json); if (jraw(json) < jend(json)) return count_nested(jraw(json), jend(json)); return 0; } static const uint8_t typetoks[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,3,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0, 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,6,0,0,0,0, }; JSON_EXTERN enum json_type json_type(struct json json) { return jraw(json) < jend(json) ? typetoks[*jraw(json)] : JSON_NULL; } JSON_EXTERN struct json json_ensure(struct json json) { return jmake(jinfo(json), jraw(json), jend(json), json_raw_length(json)); } static int strcmpn(const char *a, size_t alen, const char *b, size_t blen) { size_t n = alen < blen ? alen : blen; int cmp = strncmp(a, b, n); if (cmp == 0) { cmp = alen < blen ? -1 : alen > blen ? 1 : 0; } return cmp; } static const uint8_t hextoks[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; static uint32_t decode_hex(const uint8_t *str) { return (((int)hextoks[str[0]])<<12) | (((int)hextoks[str[1]])<<8) | (((int)hextoks[str[2]])<<4) | (((int)hextoks[str[3]])<<0); } static bool is_surrogate(uint32_t cp) { return cp > 55296 && cp < 57344; } static uint32_t decode_codepoint(uint32_t cp1, uint32_t cp2) { return cp1 > 55296 && cp1 < 56320 && cp2 > 56320 && cp2 < 57344 ? ((cp1 - 55296) << 10) | ((cp2 - 56320) + 65536) : 65533; } static inline int encode_codepoint(uint8_t dst[], uint32_t cp) { if (cp < 128) { dst[0] = cp; return 1; } else if (cp < 2048) { dst[0] = 192 | (cp >> 6); dst[1] = 128 | (cp & 63); return 2; } else if (cp > 1114111 || is_surrogate(cp)) { cp = 65533; // error codepoint } if (cp < 65536) { dst[0] = 224 | (cp >> 12); dst[1] = 128 | ((cp >> 6) & 63); dst[2] = 128 | (cp & 63); return 3; } dst[0] = 240 | (cp >> 18); dst[1] = 128 | ((cp >> 12) & 63); dst[2] = 128 | ((cp >> 6) & 63); dst[3] = 128 | (cp & 63); return 4; } // for_each_utf8 iterates over each UTF-8 bytes in jstr, unescaping along the // way. 'f' is a loop expression that will make available the 'ch' char which // is just a single byte in a UTF-8 series. #define for_each_utf8(jstr, len, f) { \ size_t nn = (len); \ int ch = 0; \ (void)ch; \ for (size_t ii = 0; ii < nn; ii++) { \ if ((jstr)[ii] != '\\') { \ ch = (jstr)[ii]; \ if (1) f \ continue; \ }; \ ii++; \ if (ii == nn) break; \ switch ((jstr)[ii]) { \ case '\\': ch = '\\'; break; \ case '/' : ch = '/'; break; \ case 'b' : ch = '\b'; break; \ case 'f' : ch = '\f'; break; \ case 'n' : ch = '\n'; break; \ case 'r' : ch = '\r'; break; \ case 't' : ch = '\t'; break; \ case '"' : ch = '"'; break; \ case 'u' : \ if (ii+5 > nn) { nn = 0; continue; }; \ uint32_t cp = decode_hex((jstr)+ii+1); \ ii += 5; \ if (is_surrogate(cp)) { \ if (nn-ii >= 6 && (jstr)[ii] == '\\' && (jstr)[ii+1] == 'u') { \ cp = decode_codepoint(cp, decode_hex((jstr)+ii+2)); \ ii += 6; \ } \ } \ uint8_t _bytes[4]; \ int _n = encode_codepoint(_bytes, cp); \ for (int _j = 0; _j < _n; _j++) { \ ch = _bytes[_j]; \ if (1) f \ } \ ii--; \ continue; \ default: \ continue; \ }; \ if (1) f \ } \ } JSON_EXTERN int json_raw_comparen(struct json json, const char *str, size_t len) { char *raw = (char*)jraw(json); if (!raw) raw = ""; size_t rlen = json_raw_length(json); return strcmpn(raw, rlen, str, len); } JSON_EXTERN int json_raw_compare(struct json json, const char *str) { return json_raw_comparen(json, str, strlen(str)); } JSON_EXTERN size_t json_string_length(struct json json) { size_t len = json_raw_length(json); if (json_type(json) != JSON_STRING) { return len; } len = len < 2 ? 0 : len - 2; if ((jinfo(json)&IESC) != IESC) { return len; } uint8_t *raw = jraw(json)+1; size_t count = 0; for_each_utf8(raw, len, { count++; }); return count; } JSON_EXTERN int json_string_comparen(struct json json, const char *str, size_t slen) { if (json_type(json) != JSON_STRING) { return json_raw_comparen(json, str, slen); } uint8_t *raw = jraw(json); size_t rlen = json_raw_length(json); raw++; rlen = rlen < 2 ? 0 : rlen - 2; if ((jinfo(json)&IESC) != IESC) { return strcmpn((char*)raw, rlen, str, slen); } int cmp = 0; uint8_t *sp = (uint8_t*)(str ? str : ""); for_each_utf8(raw, rlen, { if (!*sp || ch > *sp) { cmp = 1; goto done; } else if (ch < *sp) { cmp = -1; goto done; } sp++; }); done: if (cmp == 0 && *sp) cmp = -1; return cmp; } JSON_EXTERN int json_string_compare(struct json json, const char *str) { return json_string_comparen(json, str, str?strlen(str):0); } JSON_EXTERN size_t json_string_copy(struct json json, char *str, size_t n) { size_t len = json_raw_length(json); uint8_t *raw = jraw(json); bool isjsonstr = json_type(json) == JSON_STRING; bool isesc = false; if (isjsonstr) { raw++; len = len < 2 ? 0 : len - 2; isesc = (jinfo(json)&IESC) == IESC; } if (!isesc) { if (n == 0) return len; n = n-1 < len ? n-1 : len; memcpy(str, raw, n); str[n] = '\0'; return len; } size_t count = 0; for_each_utf8(raw, len, { if (count < n) str[count] = ch; count++; }); if (n > count) str[count] = '\0'; else if (n > 0) str[n-1] = '\0'; return count; } JSON_EXTERN size_t json_array_count(struct json json) { size_t count = 0; if (json_type(json) == JSON_ARRAY) { json = json_first(json); while (json_exists(json)) { count++; json = json_next(json); } } return count; } JSON_EXTERN struct json json_array_get(struct json json, size_t index) { if (json_type(json) == JSON_ARRAY) { json = json_first(json); while (json_exists(json)) { if (index == 0) return json; json = json_next(json); index--; } } return (struct json) { 0 }; } JSON_EXTERN struct json json_object_getn(struct json json, const char *key, size_t len) { if (json_type(json) == JSON_OBJECT) { json = json_first(json); while (json_exists(json)) { if (json_string_comparen(json, key, len) == 0) { return json_next(json); } json = json_next(json_next(json)); } } return (struct json) { 0 }; } JSON_EXTERN struct json json_object_get(struct json json, const char *key) { return json_object_getn(json, key, key?strlen(key):0); } static double stod(const uint8_t *str, size_t len, char *buf) { memcpy(buf, str, len); buf[len] = '\0'; char *ptr; double x = strtod(buf, &ptr); return (size_t)(ptr-buf) == len ? x : 0; } static double parse_double_big(const uint8_t *str, size_t len) { char buf[512]; if (len >= sizeof(buf)) return 0; return stod(str, len, buf); } static double parse_double(const uint8_t *str, size_t len) { char buf[32]; if (len >= sizeof(buf)) return parse_double_big(str, len); return stod(str, len, buf); } static int64_t parse_int64(const uint8_t *s, size_t len) { char buf[21]; double y; if (len == 0) return 0; if (len < sizeof(buf) && sizeof(long long) == sizeof(int64_t)) { memcpy(buf, s, len); buf[len] = '\0'; char *ptr = NULL; int64_t x = strtoll(buf, &ptr, 10); if ((size_t)(ptr-buf) == len) return x; y = strtod(buf, &ptr); if ((size_t)(ptr-buf) == len) goto clamp; } y = parse_double(s, len); clamp: if (y < (double)INT64_MIN) return INT64_MIN; if (y > (double)INT64_MAX) return INT64_MAX; return y; } static uint64_t parse_uint64(const uint8_t *s, size_t len) { char buf[21]; double y; if (len == 0) return 0; if (len < sizeof(buf) && sizeof(long long) == sizeof(uint64_t) && s[0] != '-') { memcpy(buf, s, len); buf[len] = '\0'; char *ptr = NULL; uint64_t x = strtoull(buf, &ptr, 10); if ((size_t)(ptr-buf) == len) return x; y = strtod(buf, &ptr); if ((size_t)(ptr-buf) == len) goto clamp; } y = parse_double(s, len); clamp: if (y < 0) return 0; if (y > (double)UINT64_MAX) return UINT64_MAX; return y; } JSON_EXTERN double json_double(struct json json) { switch (json_type(json)) { case JSON_TRUE: return 1; case JSON_STRING: if (jlen(json) < 3) return 0.0; return parse_double(jraw(json)+1, jlen(json)-2); case JSON_NUMBER: return parse_double(jraw(json), jlen(json)); default: return 0.0; } } JSON_EXTERN int64_t json_int64(struct json json) { switch (json_type(json)) { case JSON_TRUE: return 1; case JSON_STRING: if (jlen(json) < 2) return 0; return parse_int64(jraw(json)+1, jlen(json)-2); case JSON_NUMBER: return parse_int64(jraw(json), jlen(json)); default: return 0; } } JSON_EXTERN int json_int(struct json json) { int64_t x = json_int64(json); if (x < (int64_t)INT_MIN) return INT_MIN; if (x > (int64_t)INT_MAX) return INT_MAX; return x; } JSON_EXTERN uint64_t json_uint64(struct json json) { switch (json_type(json)) { case JSON_TRUE: return 1; case JSON_STRING: if (jlen(json) < 2) return 0; return parse_uint64(jraw(json)+1, jlen(json)-2); case JSON_NUMBER: return parse_uint64(jraw(json), jlen(json)); default: return 0; } } JSON_EXTERN bool json_bool(struct json json) { switch (json_type(json)) { case JSON_TRUE: return true; case JSON_NUMBER: return json_double(json) != 0.0; case JSON_STRING: { char *trues[] = { "1", "t", "T", "true", "TRUE", "True" }; for (size_t i = 0; i < sizeof(trues)/sizeof(char*); i++) { if (json_string_compare(json, trues[i]) == 0) return true; } return false; } default: return false; } } struct jesc_buf { uint8_t *esc; size_t esclen; size_t count; }; static void jesc_append(struct jesc_buf *buf, uint8_t ch) { if (buf->esclen > 1) { *(buf->esc++) = ch; buf->esclen--; } buf->count++; } static void jesc_append2(struct jesc_buf *buf, uint8_t c1, uint8_t c2) { jesc_append(buf, c1); jesc_append(buf, c2); } static const uint8_t hexchars[] = "0123456789abcdef"; static void jesc_append_ux(struct jesc_buf *buf, uint8_t c1, uint8_t c2, uint16_t x) { jesc_append2(buf, c1, c2); jesc_append2(buf, hexchars[x>>12&0xF], hexchars[x>>8&0xF]); jesc_append2(buf, hexchars[x>>4&0xF], hexchars[x>>0&0xF]); } JSON_EXTERN size_t json_escapen(const char *str, size_t len, char *esc, size_t n) { uint8_t cpbuf[4]; struct jesc_buf buf = { .esc = (uint8_t*)esc, .esclen = n }; jesc_append(&buf, '"'); for (size_t i = 0; i < len; i++) { uint32_t c = (uint8_t)str[i]; if (c < ' ') { switch (c) { case '\n': jesc_append2(&buf, '\\', 'n'); break; case '\b': jesc_append2(&buf, '\\', 'b'); break; case '\f': jesc_append2(&buf, '\\', 'f'); break; case '\r': jesc_append2(&buf, '\\', 'r'); break; case '\t': jesc_append2(&buf, '\\', 't'); break; default: jesc_append_ux(&buf, '\\', 'u', c); } } else if (c == '>' || c == '<' || c == '&') { // make web safe jesc_append_ux(&buf, '\\', 'u', c); } else if (c == '\\') { jesc_append2(&buf, '\\', '\\'); } else if (c == '"') { jesc_append2(&buf, '\\', '"'); } else if (c > 127) { struct vutf8res res = vutf8((uint8_t*)(str+i), len-i); if (res.n == 0) { res.n = 1; res.cp = 0xfffd; } int cpn = encode_codepoint(cpbuf, res.cp); for (int j = 0; j < cpn; j++) { jesc_append(&buf, cpbuf[j]); } i = i + res.n - 1; } else { jesc_append(&buf, str[i]); } } jesc_append(&buf, '"'); if (buf.esclen > 0) { // add to null terminator *(buf.esc++) = '\0'; buf.esclen--; } return buf.count; } JSON_EXTERN size_t json_escape(const char *str, char *esc, size_t n) { return json_escapen(str, str?strlen(str):0, esc, n); } JSON_EXTERN struct json json_getn(const char *json_str, size_t len, const char *path) { if (!path) return (struct json) { 0 }; struct json json = json_parsen(json_str, len); int i = 0; bool end = false; char *p = (char*)path; for (; !end && json_exists(json); i++) { // get the next component const char *key = p; while (*p && *p != '.') p++; size_t klen = p-key; if (*p == '.') p++; else if (!*p) end = true; enum json_type type = json_type(json); if (type == JSON_OBJECT) { json = json_object_getn(json, key, klen); } else if (type == JSON_ARRAY) { if (klen == 0) { i = 0; break; } char *end; size_t index = strtol(key, &end, 10); if (*end && *end != '.') { i = 0; break; } json = json_array_get(json, index); } else { i = 0; break; } } return i == 0 ? (struct json) { 0 } : json; } JSON_EXTERN struct json json_get(const char *json_str, const char *path) { return json_getn(json_str, json_str?strlen(json_str):0, path); } JSON_EXTERN bool json_string_is_escaped(struct json json) { return (jinfo(json)&IESC) == IESC; } // END json.c #if defined(__GNUC__) #pragma GCC diagnostic pop #endif #endif static struct tg_geom *make_parse_error(const char *format, ...) { char *error = NULL; va_list args; va_start(args, format); int n = vsnprintf(NULL, 0, format, args); va_end(args); if (n >= 0) { error = tg_malloc(n+1); if (error) { va_start(args, format); vsnprintf(error, n+1, format, args); va_end(args); } } if (!error) return NULL; struct tg_geom *geom = geom_new_empty(TG_POINT); if (!geom) { tg_free(error); return NULL; } setflags(geom, IS_ERROR); geom->error = error; return geom; } /// Return a parsing error. /// /// Parsing functions, such as tg_parse_geojson(), may fail due to invalid /// input data. /// /// It's important to **always** check for errors after parsing. /// /// **Example** /// /// ``` /// struct tg_geom *geom = tg_parse_geojson(input); /// if (tg_geom_error(geom)) { /// // The parsing failed due to invalid input or out of memory. /// /// // Get the error message. /// const char *err = tg_geom_error(geom); /// /// // Do something with the error, such as log it. /// printf("[err] %s\n", err); /// /// // Make sure to free the error geom and it's resources. /// tg_geom_free(geom); /// /// // !! /// // DO NOT use the return value of tg_geom_error() after calling /// // tg_geom_free(). If you need to hang onto the error for a longer /// // period of time then you must copy it before freeing. /// // !! /// /// return; /// } else { /// // ... Parsing succeeded /// } /// ``` /// /// @return A string describing the error /// @return NULL if there was no error /// @see tg_geom_free() /// @see tg_parse_geojson() /// @see tg_parse_wkt() /// @see tg_parse_wkb() /// @see tg_parse_hex() /// @see GeometryParsing const char *tg_geom_error(const struct tg_geom *geom) { if (!geom) return "no memory"; return (getflags(geom)&IS_ERROR) == IS_ERROR ? geom->error : NULL; } static bool buf_append_json_pair(struct tg_buf *buf, struct json key, struct json val) { size_t len = buf->len; if (!tg_buf_append_byte(buf, buf->len == 0 ? '{' : ',') || !tg_buf_append_bytes(buf, (uint8_t*)json_raw(key), json_raw_length(key)) || !tg_buf_append_byte(buf, ':') || !tg_buf_append_bytes(buf, (uint8_t*)json_raw(val), json_raw_length(val))) { buf->len = len; return false; } return true; } // returns an error message constant if there's an error. Do not free. static const char *take_basic_geojson(struct json json, const char *target_name, struct json *targetout, enum flags *flagsout, char **extraout, bool *okout ) { enum flags flags = 0; bool is_feat = strcmp(target_name, "geometry") == 0; const char *err = NULL; bool ok = false; bool has_props = false; bool has_id = false; struct tg_buf extra = { 0 }; struct json target = { 0 }; struct json key = json_first(json); struct json val = json_next(key); while (json_exists(key)) { if (json_string_compare(key, "type") == 0) { // .. do nothing, the caller should already know its type ... } else if (json_string_compare(key, target_name) == 0) { val = json_ensure(val); target = val; } else { bool skip_val = false; if (is_feat) { if (json_string_compare(key, "properties") == 0) { if (!has_props) { if (json_type(val) == JSON_NULL) { flags |= HAS_NULL_PROPS; skip_val = true; } else if (json_type(val) == JSON_OBJECT) { skip_val = !json_exists(json_first(val)); } else { err = "'properties' must be an object or null"; goto fail; } } else { skip_val = true; } has_props = true; } else if (json_string_compare(key, "id") == 0) { if (has_id) { skip_val = true; } else { if (json_type(val) != JSON_STRING && json_type(val) != JSON_NUMBER) { err = "'id' must be a string or number"; goto fail; } } has_id = true; } } if (!skip_val) { if (!buf_append_json_pair(&extra, key, val)) goto fail; } } key = json_next(val); val = json_next(key); } if (!json_exists(target)) { if (strcmp(target_name, "geometry") == 0) { err = "missing 'geometry'"; } else if (strcmp(target_name, "geometries") == 0) { err = "missing 'geometries'"; } else if (strcmp(target_name, "features") == 0) { err = "missing 'features'"; } else { // "coordinates" err = "missing 'coordinates'"; } goto fail; } enum json_type ttype = json_type(target); if (is_feat) { if (ttype != JSON_OBJECT) { if (ttype == JSON_NULL) { // unlocated // https://www.rfc-editor.org/rfc/rfc7946#section-3.2 flags |= IS_EMPTY; flags |= IS_UNLOCATED; } else { err = "'geometry' must be an object or null"; goto fail; } } #ifdef GEOJSON_REQ_PROPERTIES if (!has_props) { err = "missing 'properties'"; goto fail; } #endif } else if (ttype != JSON_ARRAY) { if (strcmp(target_name, "geometries") == 0) { err = "'geometries' must be an array"; } else if (strcmp(target_name, "features") == 0) { err = "'features' must be an array"; } else { // "coordinates" err = "'coordinates' must be an array"; } goto fail; } else if (!json_exists(json_first(target))) { // null object // https://www.rfc-editor.org/rfc/rfc7946#section-3.1 flags |= IS_EMPTY; } if (extra.len > 0) { if (!tg_buf_append_byte(&extra, '}')) goto fail; if (!tg_buf_append_byte(&extra, '\0')) goto fail; if (!tg_buf_trunc(&extra)) goto fail; } ok = true; fail: if (!ok) { if (extra.data) tg_free(extra.data); *extraout = NULL; } else { *extraout = (char*)extra.data; } *targetout = target; *okout = ok; *flagsout = flags; return err; } #define def_vec(name, type, append_name, start_cap) \ name { \ type *data; \ size_t len; \ size_t cap; \ }; \ static bool append_name(name *vec, type f) { \ if (vec->len == vec->cap) { \ size_t cap = vec->cap; \ cap = grow_cap(cap, start_cap); \ type *data = tg_realloc(vec->data, cap*sizeof(type)); \ if (!data) return false; \ vec->data = data; \ vec->cap = cap; \ } \ vec->data[vec->len++] = f; \ return true; \ } \ // some vectors are better than no vectors, i guess. def_vec(struct dvec, double, dvec_append, 8) def_vec(struct rvec, struct tg_ring*, rvec_append, 1) def_vec(struct lvec, struct tg_line*, lvec_append, 1) def_vec(struct pvec, struct tg_poly*, pvec_append, 1) def_vec(struct gvec, struct tg_geom*, gvec_append, 1) #define PARSE_GEOJSON_BASIC_HEAD(target_name) \ struct tg_geom *gerr = NULL; \ struct tg_geom *geom = NULL; \ struct json target; \ enum flags flags; \ char *extra; \ bool ok; \ const char *err_ = take_basic_geojson(json, target_name, \ &target, &flags, &extra, &ok); \ if (!ok) { \ gerr = err_ ? make_parse_error("%s", err_) : NULL; \ goto fail; \ } #define PARSE_GEOJSON_BASIC_TAIL(cleanup) \ goto done; \ done: \ if (!geom) goto fail; \ setflags(geom, flags); \ if (extra) geom->xjson = extra; \ cleanup; \ return geom; \ fail: \ tg_geom_free(geom); \ if (extra) tg_free(extra); \ cleanup; \ return gerr; static const char *err_for_geojson_depth(int depth) { if (depth == 1) { return "'coordinates' must be an array of positions"; } else if (depth == 2) { return "'coordinates' must be a two deep nested array of positions"; } else { return "'coordinates' must be a three deep nested array of positions"; } } static struct tg_geom *parse_geojson_point(struct json json, bool req_geom, enum tg_index ix) { (void)ix; PARSE_GEOJSON_BASIC_HEAD("coordinates") if ((flags&IS_EMPTY) == IS_EMPTY) { geom = tg_geom_new_point_empty(); if (!geom) goto fail; goto done; } double posn[4]; int dims = 0; struct json val = json_first(target); while (json_exists(val)) { if (json_type(val) != JSON_NUMBER) { gerr = make_parse_error("'coordinates' must only contain numbers"); goto fail; } if (dims < 4) { // i don't care about more than 4 dimensions posn[dims] = json_double(val); dims++; } val = json_next(val); } if (dims < 2) { gerr = make_parse_error("'coordinates' must have two or more numbers"); goto fail; } struct tg_point xy = { posn[0], posn[1] }; double z = posn[2]; double m = posn[3]; if (!req_geom && !extra && dims == 2) { geom = tg_geom_new_point(xy); } else { switch (dims) { case 2: geom = geom_new(TG_POINT); if (!geom) goto fail; geom->point = xy; break; case 3: geom = tg_geom_new_point_z(xy, z); break; default: geom = tg_geom_new_point_zm(xy, z, m); break; } } PARSE_GEOJSON_BASIC_TAIL() } static bool check_parse_posns(enum base base, double *posns, int nposns, const char **err) { // nposns must be an even number. const struct tg_point *points = (void*)posns; int npoints = nposns / 2; if (base == BASE_LINE) { if (npoints < 2) { *err = "lines must have two or more positions"; return false; } } else if (base == BASE_RING) { if (npoints < 3) { *err = "rings must have three or more positions"; return false; } struct tg_point p0 = points[0]; struct tg_point p1 = points[npoints-1]; if (!(p0.x == p1.x && p0.y == p1.y)) { *err = "rings must have matching first and last positions"; return false; } } return true; } // return the dims or -1 if error static int parse_geojson_posns(enum base base, int dims, int depth, struct json coords, struct dvec *posns, struct dvec *xcoords, const char **err) { struct json val0 = json_first(coords); while (json_exists(val0)) { if (json_type(val0) != JSON_ARRAY) { *err = err_for_geojson_depth(depth); return -1; } struct json val1 = json_first(val0); double posn[4]; int pdims = 0; while (json_exists(val1)) { if (json_type(val1) != JSON_NUMBER) { *err = "each element in a position must be a number"; return -1; } if (pdims < 4) { // we don't care more that 4 posn[pdims] = json_double(val1); pdims++; } val1 = json_next(val1); } if (dims == 0) { dims = pdims; } if (pdims < 2) { *err = "each position must have two or more numbers"; return -1; } else if (pdims != dims) { *err = "each position must have the same number of dimensions"; return -1; } if (!dvec_append(posns, posn[0]) || !dvec_append(posns, posn[1])) { return -1; } for (int i = 2; i < dims; i++) { if (i >= pdims || !dvec_append(xcoords, posn[i])) return -1; // if (!dvec_append(xcoords, i < pdims ? posn[i] : 0)) return -1; } val0 = json_next(val0); } if (!check_parse_posns(base, posns->data, posns->len, err)) return -1; return dims; } static struct tg_geom *parse_geojson_linestring(struct json json, bool req_geom, enum tg_index ix) { struct tg_line *line = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; const char *err = NULL; PARSE_GEOJSON_BASIC_HEAD("coordinates") if ((flags&IS_EMPTY) == IS_EMPTY) { geom = tg_geom_new_linestring_empty(); goto done; } int dims = parse_geojson_posns(BASE_LINE, 0, 1, target, &posns, &xcoords, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } line = tg_line_new_ix((struct tg_point*)posns.data, posns.len / 2, ix); if (!line) goto fail; if (!req_geom && !extra && dims == 2) { geom = tg_geom_new_linestring(line); } else { switch (dims) { case 2: geom = geom_new(TG_LINESTRING); if (geom) geom->line = tg_line_clone(line); break; case 3: geom = tg_geom_new_linestring_z(line, xcoords.data, xcoords.len); break; default: geom = tg_geom_new_linestring_zm(line, xcoords.data, xcoords.len); break; } } PARSE_GEOJSON_BASIC_TAIL({ if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); if (line) tg_line_free(line); }) } // returns the dims or -1 if error static int parse_geojson_multi_posns(enum base base, int dims, int depth, struct json coords, struct dvec *posns, struct rvec *rings, struct tg_poly **poly, struct dvec *xcoords, enum tg_index ix, const char **err) { struct json val0 = json_first(coords); while (json_exists(val0)) { if (json_type(val0) != JSON_ARRAY) { *err = "'coordinates' must be a nested array"; return -1; } posns->len = 0; dims = parse_geojson_posns(base, dims, depth, val0, posns, xcoords, err); if (dims == -1) return -1; struct tg_ring *ring = tg_ring_new_ix((struct tg_point*)posns->data, posns->len / 2, ix); if (!ring) return -1; if (!rvec_append(rings, ring)) { tg_ring_free(ring); return -1; } val0 = json_next(val0); } if (rings->len == 0) { *err = "polygons must have one or more rings"; return -1; } *poly = tg_poly_new(rings->data[0], (struct tg_ring const*const*)rings->data+1, rings->len-1); if (!*poly) return -1; for (size_t i = 0; i < rings->len; i++) { tg_ring_free(rings->data[i]); } rings->len = 0; return dims; } static struct tg_geom *parse_geojson_polygon(struct json json, bool req_geom, enum tg_index ix) { struct tg_poly *poly = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct rvec rings = { 0 }; const char *err = NULL; PARSE_GEOJSON_BASIC_HEAD("coordinates") if ((flags&IS_EMPTY) == IS_EMPTY) { geom = tg_geom_new_polygon_empty(); goto done; } int dims = parse_geojson_multi_posns(BASE_RING, 0, 2, target, &posns, &rings, &poly, &xcoords, ix, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } if (!req_geom && !extra && dims == 2) { geom = tg_geom_new_polygon(poly); } else { switch (dims) { case 2: geom = geom_new(TG_POLYGON); if (geom) geom->poly = tg_poly_clone(poly); break; case 3: geom = tg_geom_new_polygon_z(poly, xcoords.data, xcoords.len); break; default: geom = tg_geom_new_polygon_zm(poly, xcoords.data, xcoords.len); break; } } PARSE_GEOJSON_BASIC_TAIL({ if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); if (rings.data) { for (size_t i = 0; i < rings.len; i++) { tg_ring_free(rings.data[i]); } tg_free(rings.data); } if (poly) tg_poly_free(poly); }) } static struct tg_geom *parse_geojson_multipoint(struct json json, enum tg_index ix) { (void)ix; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; const char *err = NULL; PARSE_GEOJSON_BASIC_HEAD("coordinates") int dims = parse_geojson_posns(BASE_POINT, 0, 1, target, &posns, &xcoords, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } const struct tg_point *points = (struct tg_point*)posns.data; int npoints = posns.len/2; switch (dims) { case 2: geom = tg_geom_new_multipoint(points, npoints); break; case 3: geom = tg_geom_new_multipoint_z(points, npoints, xcoords.data, xcoords.len); break; default: geom = tg_geom_new_multipoint_zm(points, npoints, xcoords.data, xcoords.len); break; } PARSE_GEOJSON_BASIC_TAIL({ if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); }) } static struct tg_geom *parse_geojson_multilinestring(struct json json, enum tg_index ix) { struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct lvec lines = { 0 }; const char *err = NULL; int dims = 0; PARSE_GEOJSON_BASIC_HEAD("coordinates") struct json val0 = json_first(target); while (json_exists(val0)) { if (json_type(val0) != JSON_ARRAY) { gerr = make_parse_error("%s", err_for_geojson_depth(2)); goto fail; } posns.len = 0; dims = parse_geojson_posns(BASE_LINE, dims, 2, val0, &posns, &xcoords, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } struct tg_line *line = tg_line_new_ix((struct tg_point*)posns.data, posns.len / 2, ix); if (!line) goto fail; if (!lvec_append(&lines, line)) { tg_line_free(line); goto fail; } val0 = json_next(val0); } switch (dims) { case 2: geom = tg_geom_new_multilinestring( (struct tg_line const*const*)lines.data, lines.len); break; case 3: geom = tg_geom_new_multilinestring_z( (struct tg_line const*const*)lines.data, lines.len, xcoords.data, xcoords.len); break; default: geom = tg_geom_new_multilinestring_zm( (struct tg_line const*const*)lines.data, lines.len, xcoords.data, xcoords.len); break; } PARSE_GEOJSON_BASIC_TAIL({ if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); if (lines.data) { for (size_t i = 0; i < lines.len; i++) { tg_line_free(lines.data[i]); } tg_free(lines.data); } }) } static struct tg_geom *parse_geojson_multipolygon(struct json json, enum tg_index ix) { struct tg_poly *poly = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct rvec rings = { 0 }; struct pvec polys = { 0 }; const char *err = NULL; int dims = 0; PARSE_GEOJSON_BASIC_HEAD("coordinates") struct json val0 = json_first(target); while (json_exists(val0)) { if (json_type(val0) != JSON_ARRAY) { gerr = make_parse_error("%s", err_for_geojson_depth(3)); goto fail; } posns.len = 0; rings.len = 0; dims = parse_geojson_multi_posns(BASE_RING, dims, 3, val0, &posns, &rings, &poly, &xcoords, ix, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } if (!pvec_append(&polys, poly)) { tg_poly_free(poly); goto fail; } val0 = json_next(val0); } switch (dims) { case 2: geom = tg_geom_new_multipolygon( (struct tg_poly const*const*)polys.data, polys.len); break; case 3: geom = tg_geom_new_multipolygon_z( (struct tg_poly const*const*)polys.data, polys.len, xcoords.data, xcoords.len); break; default: geom = tg_geom_new_multipolygon_zm( (struct tg_poly const*const*)polys.data, polys.len, xcoords.data, xcoords.len); break; } PARSE_GEOJSON_BASIC_TAIL({ if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); if (rings.data) { for (size_t i = 0; i < rings.len; i++) { tg_ring_free(rings.data[i]); } tg_free(rings.data); } if (polys.data) { for (size_t i = 0; i < polys.len; i++) { tg_poly_free(polys.data[i]); } tg_free(polys.data); } }) } static struct tg_geom *parse_geojson(struct json json, bool req_geom, enum tg_index ix); static struct tg_geom *parse_geojson_geometrycollection(struct json json, enum tg_index ix) { struct gvec geoms = { 0 }; PARSE_GEOJSON_BASIC_HEAD("geometries") struct json val0 = json_first(target); while (json_exists(val0)) { struct tg_geom *child = parse_geojson(val0, false, ix); if (!child) goto fail; if (tg_geom_error(child)) { gerr = child; child = NULL; goto fail; } if ((getflags(child)&IS_FEATURE) == IS_FEATURE || (getflags(child)&IS_FEATURE_COL) == IS_FEATURE_COL) { gerr = make_parse_error("'geometries' must only contain objects " "with the 'type' of Point, LineString, Polygon, " "MultiPoint, MultiLineString, MultiPolygon, or " "GeometryCollection"); tg_geom_free(child); goto fail; } if (!gvec_append(&geoms, child)) { tg_geom_free(child); goto fail; } val0 = json_next(val0); } geom = tg_geom_new_geometrycollection( (struct tg_geom const*const*)geoms.data, geoms.len); PARSE_GEOJSON_BASIC_TAIL({ if (geoms.data) { for (size_t i = 0; i < geoms.len; i++) { tg_geom_free(geoms.data[i]); } tg_free(geoms.data); } }) } static struct tg_geom *parse_geojson_feature(struct json json, enum tg_index ix) { struct tg_buf combined = { 0 }; PARSE_GEOJSON_BASIC_HEAD("geometry") if ((flags&IS_EMPTY) == IS_EMPTY) { geom = tg_geom_new_point_empty(); } else { geom = parse_geojson(target, extra != NULL, ix); } if (!geom) goto fail; if (tg_geom_error(geom)) { gerr = geom; geom = NULL; goto fail; } if ((getflags(geom)&IS_FEATURE) == IS_FEATURE || (getflags(geom)&IS_FEATURE_COL) == IS_FEATURE_COL) { gerr = make_parse_error("'geometry' must only contain an object with " "the 'type' of Point, LineString, Polygon, MultiPoint, " "MultiLineString, MultiPolygon, or GeometryCollection"); goto fail; } setflags(geom, IS_FEATURE); if (getbase(geom) == BASE_GEOM && geom->xjson) { // combine the two together as '[feature-extra,geometry-extra]' size_t xn0 = extra ? strlen(extra) : 0; size_t xn1 = strlen(geom->xjson); if (!tg_buf_append_byte(&combined, '[') || !tg_buf_append_bytes(&combined, (uint8_t*)(xn0 ? extra : "{}"), (xn0 ? xn0 : 2)) || !tg_buf_append_byte(&combined, ',') || !tg_buf_append_bytes(&combined, (uint8_t*)geom->xjson, xn1) || !tg_buf_append_byte(&combined, ']') || !tg_buf_append_byte(&combined, '\0')) { goto fail; } if (!tg_buf_trunc(&combined)) goto fail; if (geom->xjson) tg_free(geom->xjson); geom->xjson = NULL; if (extra) tg_free(extra); extra = (char*)combined.data; combined = (struct tg_buf) { 0 }; } PARSE_GEOJSON_BASIC_TAIL({ if (combined.data) tg_free(combined.data); }) } static struct tg_geom *parse_geojson_featurecollection(struct json json, enum tg_index ix) { struct gvec geoms = { 0 }; PARSE_GEOJSON_BASIC_HEAD("features") struct json val0 = json_first(target); while (json_exists(val0)) { struct tg_geom *child = parse_geojson(val0, false, ix); if (!child) goto fail; if (tg_geom_error(child)) { gerr = child; goto fail; } if ((getflags(child)&IS_FEATURE) != IS_FEATURE) { gerr = make_parse_error("'features' must only contain objects " "with the 'type' of Feature"); tg_geom_free(child); goto fail; } if (!gvec_append(&geoms, child)) { tg_geom_free(child); goto fail; } val0 = json_next(val0); } geom = tg_geom_new_geometrycollection( (struct tg_geom const*const*)geoms.data, geoms.len); if (geom) setflags(geom, IS_FEATURE_COL); PARSE_GEOJSON_BASIC_TAIL({ if (geoms.data) { for (size_t i = 0; i < geoms.len; i++) { tg_geom_free(geoms.data[i]); } tg_free(geoms.data); } }) } static struct tg_geom *parse_geojson(struct json json, bool req_geom, enum tg_index ix) { if (json_type(json) != JSON_OBJECT) { return make_parse_error("expected an object"); } struct json jtype = json_object_get(json, "type"); if (!json_exists(jtype)) { return make_parse_error("'type' is required"); } char type[24]; json_string_copy(jtype, type, sizeof(type)); struct tg_geom *geom = NULL; if (strcmp(type, "Point") == 0) { geom = parse_geojson_point(json, req_geom, ix); } else if (strcmp(type, "LineString") == 0) { geom = parse_geojson_linestring(json, req_geom, ix); } else if (strcmp(type, "Polygon") == 0) { geom = parse_geojson_polygon(json, req_geom, ix); } else if (strcmp(type, "MultiPoint") == 0) { geom = parse_geojson_multipoint(json, ix); } else if (strcmp(type, "MultiLineString") == 0) { geom = parse_geojson_multilinestring(json, ix); } else if (strcmp(type, "MultiPolygon") == 0) { geom = parse_geojson_multipolygon(json, ix); } else if (strcmp(type, "GeometryCollection") == 0) { geom = parse_geojson_geometrycollection(json, ix); } else if (strcmp(type, "Feature") == 0) { geom = parse_geojson_feature(json, ix); } else if (strcmp(type, "FeatureCollection") == 0) { geom = parse_geojson_featurecollection(json, ix); } else { geom = make_parse_error("unknown type '%s'", type); } return geom; } /// Parse geojson with an included data length. /// @param geojson Geojson data. Must be UTF8. /// @param len Length of data /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_geojson() /// @see GeometryParsing struct tg_geom *tg_parse_geojsonn(const char *geojson, size_t len) { return tg_parse_geojsonn_ix(geojson, len, TG_DEFAULT); } /// Parse geojson. /// /// Supports [GeoJSON](https://datatracker.ietf.org/doc/html/rfc7946) standard, /// including Features, FeaturesCollection, ZM coordinates, properties, and /// arbitrary JSON members. /// @param geojson A geojson string. Must be UTF8 and null-terminated. /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_geojsonn() /// @see tg_parse_geojson_ix() /// @see tg_parse_geojsonn_ix() /// @see tg_geom_error() /// @see tg_geom_geojson() /// @see GeometryParsing struct tg_geom *tg_parse_geojson(const char *geojson) { return tg_parse_geojsonn_ix(geojson, geojson?strlen(geojson):0, TG_DEFAULT); } /// Parse geojson using provided indexing option. /// @param geojson A geojson string. Must be UTF8 and null-terminated. /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see [tg_index](.#tg_index) /// @see tg_parse_geojson() /// @see tg_parse_geojsonn_ix() /// @see GeometryParsing struct tg_geom *tg_parse_geojson_ix(const char *geojson, enum tg_index ix) { return tg_parse_geojsonn_ix(geojson, geojson?strlen(geojson):0, ix); } /// Parse geojson using provided indexing option. /// @param geojson Geojson data. Must be UTF8. /// @param len Length of data /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see [tg_index](.#tg_index) /// @see tg_parse_geojson() /// @see tg_parse_geojson_ix() /// @see GeometryParsing struct tg_geom *tg_parse_geojsonn_ix(const char *geojson, size_t len, enum tg_index ix) { struct tg_geom *geom = NULL; struct json_valid is = json_validn_ex(geojson, len, 0); if (!is.valid) { geom = make_parse_error("invalid json"); } else { struct json json = json_parsen(geojson, len); geom = parse_geojson(json, false, ix); } if (!geom) return NULL; if ((getflags(geom)&IS_ERROR) == IS_ERROR) { struct tg_geom *gerr = make_parse_error("ParseError: %s", geom->error); tg_geom_free(geom); return gerr; } return geom; } struct writer { uint8_t *dst; size_t n; size_t count; }; union raw_double { uint64_t u; double d; }; static void write_nullterm(struct writer *wr) { if (wr->n > wr->count) { wr->dst[wr->count] = '\0'; } else if (wr->n > 0) { wr->dst[wr->n-1] = '\0'; } } static void write_byte(struct writer *wr, uint8_t b) { if (wr->count < wr->n) { wr->dst[wr->count] = b; } wr->count++; } static void write_char(struct writer *wr, char ch) { write_byte(wr, ch); } static void write_uint32le(struct writer *wr, uint32_t x) { for (int i = 0; i < 4; i++) { write_byte(wr, x>>(i*8)); } } static void write_uint64le(struct writer *wr, uint64_t x) { for (int i = 0; i < 8; i++) { write_byte(wr, x>>(i*8)); } } static void write_doublele(struct writer *wr, double x) { write_uint64le(wr, ((union raw_double){.d=x}).u); } static void write_string(struct writer *wr, const char *str) { const char *p = str; while (*p) write_char(wr, *(p++)); } static void write_stringn(struct writer *wr, const char *str, size_t n) { for (size_t i = 0; i < n; i++) { write_char(wr, str[i]); } } #ifdef TG_NOAMALGA #include "deps/fp.h" #else #define FP_STATIC #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif // BEGIN fp.c // https://github.com/tidwall/fp // // Copyright 2025 Joshua J Baker. All rights reserved. // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // // https://github.com/ulfjack/ryu // // Copyright 2018 Ulf Adams // // The contents of this file may be used under the terms of the Apache License, // Version 2.0. // // (See accompanying file LICENSE-Apache or copy at // http://www.apache.org/licenses/LICENSE-2.0) // // Alternatively, the contents of this file may be used under the terms of // the Boost Software License, Version 1.0. // (See accompanying file LICENSE-Boost or copy at // https://www.boost.org/LICENSE_1_0.txt) // // Unless required by applicable law or agreed to in writing, this software // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. #include #include #include #include #include #include #include #include #include #include #include #ifdef FP_STATIC #define FP_EXTERN static #endif #ifndef FP_EXTERN #define FP_EXTERN #endif // Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 3528. static inline int32_t pow5bits(const int32_t e) { // This approximation works up to the point that the multiplication // overflows at e = 3529. // If the multiplication were done in 64 bits, it would fail at 5^4004 // which is just greater than 2^9297. assert(e >= 0); assert(e <= 3528); return (int32_t) (((((uint32_t) e) * 1217359) >> 19) + 1); } // Returns floor(log_10(2^e)); requires 0 <= e <= 1650. static inline uint32_t log10Pow2(const int32_t e) { // The first value this approximation fails for is 2^1651 which is just // greater than 10^297. assert(e >= 0); assert(e <= 1650); return (((uint32_t) e) * 78913) >> 18; } // Returns floor(log_10(5^e)); requires 0 <= e <= 2620. static inline uint32_t log10Pow5(const int32_t e) { // The first value this approximation fails for is 5^2621 which is just // greater than 10^1832. assert(e >= 0); assert(e <= 2620); return (((uint32_t) e) * 732923) >> 20; } static inline int copy_special_str(char * const result, const bool sign, const bool exponent, const bool mantissa) { if (mantissa) { memcpy(result, "NaN", 3); return 3; } if (sign) { result[0] = '-'; } if (exponent) { memcpy(result + sign, "Infinity", 8); return sign + 8; } memcpy(result + sign, "0E0", 3); return sign + 3; } static inline uint64_t double_to_bits(const double d) { uint64_t bits = 0; memcpy(&bits, &d, sizeof(double)); return bits; } // A table of all two-digit numbers. This is used to speed up decimal digit // generation by copying pairs of digits into the final output. static const char DIGIT_TABLE[200] = { '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8', '0','9','1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7', '1','8','1','9','2','0','2','1','2','2','2','3','2','4','2','5','2','6', '2','7','2','8','2','9','3','0','3','1','3','2','3','3','3','4','3','5', '3','6','3','7','3','8','3','9','4','0','4','1','4','2','4','3','4','4', '4','5','4','6','4','7','4','8','4','9','5','0','5','1','5','2','5','3', '5','4','5','5','5','6','5','7','5','8','5','9','6','0','6','1','6','2', '6','3','6','4','6','5','6','6','6','7','6','8','6','9','7','0','7','1', '7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9','8','0', '8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8', '9','9', }; // ABSL avoids uint128_t on Win32 even if __SIZEOF_INT128__ is defined. // Let's do the same for now. #if defined(__SIZEOF_INT128__) && !defined(_MSC_VER) #define HAS_UINT128 typedef __uint128_t uint128_t; #endif static inline uint64_t umul128(const uint64_t a, const uint64_t b, uint64_t* const productHi) { // The casts here help MSVC to avoid calls to the __allmul library function. const uint32_t aLo = (uint32_t)a; const uint32_t aHi = (uint32_t)(a >> 32); const uint32_t bLo = (uint32_t)b; const uint32_t bHi = (uint32_t)(b >> 32); const uint64_t b00 = (uint64_t)aLo * bLo; const uint64_t b01 = (uint64_t)aLo * bHi; const uint64_t b10 = (uint64_t)aHi * bLo; const uint64_t b11 = (uint64_t)aHi * bHi; const uint32_t b00Lo = (uint32_t)b00; const uint32_t b00Hi = (uint32_t)(b00 >> 32); const uint64_t mid1 = b10 + b00Hi; const uint32_t mid1Lo = (uint32_t)(mid1); const uint32_t mid1Hi = (uint32_t)(mid1 >> 32); const uint64_t mid2 = b01 + mid1Lo; const uint32_t mid2Lo = (uint32_t)(mid2); const uint32_t mid2Hi = (uint32_t)(mid2 >> 32); const uint64_t pHi = b11 + mid1Hi + mid2Hi; const uint64_t pLo = ((uint64_t)mid2Lo << 32) | b00Lo; *productHi = pHi; return pLo; } static inline uint64_t shiftright128(const uint64_t lo, const uint64_t hi, const uint32_t dist) { // We don't need to handle the case dist >= 64 here (see above). assert(dist < 64); assert(dist > 0); return (hi << (64 - dist)) | (lo >> dist); } static inline uint64_t div5(const uint64_t x) { return x / 5; } static inline uint64_t div10(const uint64_t x) { return x / 10; } static inline uint64_t div100(const uint64_t x) { return x / 100; } static inline uint64_t div1e8(const uint64_t x) { return x / 100000000; } static inline uint64_t div1e9(const uint64_t x) { return x / 1000000000; } static inline uint32_t mod1e9(const uint64_t x) { return (uint32_t) (x - 1000000000 * div1e9(x)); } static inline uint32_t pow5Factor(uint64_t value) { const uint64_t m_inv_5 = 14757395258967641293u; const uint64_t n_div_5 = 3689348814741910323u; uint32_t count = 0; for (;;) { assert(value != 0); value *= m_inv_5; if (value > n_div_5) break; ++count; } return count; } // Returns true if value is divisible by 5^p. static inline bool multipleOfPowerOf5(const uint64_t value, const uint32_t p) { // I tried a case distinction on p, but there was no performance difference. return pow5Factor(value) >= p; } // Returns true if value is divisible by 2^p. static inline bool multipleOfPowerOf2(const uint64_t value, const uint32_t p) { assert(value != 0); assert(p < 64); // __builtin_ctzll doesn't appear to be faster here. return (value & ((1ull << p) - 1)) == 0; } // We need a 64x128-bit multiplication and a subsequent 128-bit shift. // Multiplication: // The 64-bit factor is variable and passed in, the 128-bit factor comes // from a lookup table. We know that the 64-bit factor only has 55 // significant bits (i.e., the 9 topmost bits are zeros). The 128-bit // factor only has 124 significant bits (i.e., the 4 topmost bits are // zeros). // Shift: // In principle, the multiplication result requires 55 + 124 = 179 bits to // represent. However, we then shift this value to the right by j, which is // at least j >= 115, so the result is guaranteed to fit into 179 - 115 = 64 // bits. This means that we only need the topmost 64 significant bits of // the 64x128-bit multiplication. // // There are several ways to do this: // 1. Best case: the compiler exposes a 128-bit type. // We perform two 64x64-bit multiplications, add the higher 64 bits of the // lower result to the higher result, and shift by j - 64 bits. // // We explicitly cast from 64-bit to 128-bit, so the compiler can tell // that these are only 64-bit inputs, and can map these to the best // possible sequence of assembly instructions. // x64 machines happen to have matching assembly instructions for // 64x64-bit multiplications and 128-bit shifts. // // 2. Second best case: the compiler exposes intrinsics for the x64 assembly // instructions mentioned in 1. // // 3. We only have 64x64 bit instructions that return the lower 64 bits of // the result, i.e., we have to use plain C. // Our inputs are less than the full width, so we have three options: // a. Ignore this fact and just implement the intrinsics manually. // b. Split both into 31-bit pieces, which guarantees no internal overflow, // but requires extra work upfront (unless we change the lookup table). // c. Split only the first factor into 31-bit pieces, which also guarantees // no internal overflow, but requires extra work since the intermediate // results are not perfectly aligned. #if defined(HAS_UINT128) // Best case: use 128-bit type. static inline uint64_t mulShift64(const uint64_t m, const uint64_t* const mul, const int32_t j) { const uint128_t b0 = ((uint128_t) m) * mul[0]; const uint128_t b2 = ((uint128_t) m) * mul[1]; return (uint64_t) (((b0 >> 64) + b2) >> (j - 64)); } static inline uint64_t mulShiftAll64(const uint64_t m, const uint64_t* const mul, const int32_t j, uint64_t* const vp, uint64_t* const vm, const uint32_t mmShift) { *vp = mulShift64(4 * m + 2, mul, j); *vm = mulShift64(4 * m - 1 - mmShift, mul, j); return mulShift64(4 * m, mul, j); } #else static inline uint64_t mulShift64(const uint64_t m, const uint64_t* const mul, const int32_t j) { // m is maximum 55 bits uint64_t high1; // 128 const uint64_t low1 = umul128(m, mul[1], &high1); // 64 uint64_t high0; // 64 umul128(m, mul[0], &high0); // 0 const uint64_t sum = high0 + low1; if (sum < high0) { ++high1; // overflow into high1 } return shiftright128(sum, high1, j - 64); } // This is faster if we don't have a 64x64->128-bit multiplication. static inline uint64_t mulShiftAll64(uint64_t m, const uint64_t* const mul, const int32_t j, uint64_t* const vp, uint64_t* const vm, const uint32_t mmShift) { m <<= 1; // m is maximum 55 bits uint64_t tmp; const uint64_t lo = umul128(m, mul[0], &tmp); uint64_t hi; const uint64_t mid = tmp + umul128(m, mul[1], &hi); hi += mid < tmp; // overflow into hi const uint64_t lo2 = lo + mul[0]; const uint64_t mid2 = mid + mul[1] + (lo2 < lo); const uint64_t hi2 = hi + (mid2 < mid); *vp = shiftright128(mid2, hi2, (uint32_t) (j - 64 - 1)); if (mmShift == 1) { const uint64_t lo3 = lo - mul[0]; const uint64_t mid3 = mid - mul[1] - (lo3 > lo); const uint64_t hi3 = hi - (mid3 > mid); *vm = shiftright128(mid3, hi3, (uint32_t) (j - 64 - 1)); } else { const uint64_t lo3 = lo + lo; const uint64_t mid3 = mid + mid + (lo3 < lo); const uint64_t hi3 = hi + hi + (mid3 < mid); const uint64_t lo4 = lo3 - mul[0]; const uint64_t mid4 = mid3 - mul[1] - (lo4 > lo3); const uint64_t hi4 = hi3 - (mid4 > mid3); *vm = shiftright128(mid4, hi4, (uint32_t) (j - 64)); } return shiftright128(mid, hi, (uint32_t) (j - 64 - 1)); } #endif // These tables are generated by PrintDoubleLookupTable. #define DOUBLE_POW5_INV_BITCOUNT 125 #define DOUBLE_POW5_BITCOUNT 125 #define DOUBLE_POW5_INV_TABLE_SIZE 342 #define DOUBLE_POW5_TABLE_SIZE 326 static const uint64_t DOUBLE_POW5_INV_SPLIT[DOUBLE_POW5_INV_TABLE_SIZE][2] = { { 1u, 2305843009213693952u }, { 11068046444225730970u, 1844674407370955161u }, { 5165088340638674453u, 1475739525896764129u }, { 7821419487252849886u, 1180591620717411303u }, { 8824922364862649494u, 1888946593147858085u }, { 7059937891890119595u, 1511157274518286468u }, { 13026647942995916322u, 1208925819614629174u }, { 9774590264567735146u, 1934281311383406679u }, { 11509021026396098440u, 1547425049106725343u }, { 16585914450600699399u, 1237940039285380274u }, { 15469416676735388068u, 1980704062856608439u }, { 16064882156130220778u, 1584563250285286751u }, { 9162556910162266299u, 1267650600228229401u }, { 7281393426775805432u, 2028240960365167042u }, { 16893161185646375315u, 1622592768292133633u }, { 2446482504291369283u, 1298074214633706907u }, { 7603720821608101175u, 2076918743413931051u }, { 2393627842544570617u, 1661534994731144841u }, { 16672297533003297786u, 1329227995784915872u }, { 11918280793837635165u, 2126764793255865396u }, { 5845275820328197809u, 1701411834604692317u }, { 15744267100488289217u, 1361129467683753853u }, { 3054734472329800808u, 2177807148294006166u }, { 17201182836831481939u, 1742245718635204932u }, { 6382248639981364905u, 1393796574908163946u }, { 2832900194486363201u, 2230074519853062314u }, { 5955668970331000884u, 1784059615882449851u }, { 1075186361522890384u, 1427247692705959881u }, { 12788344622662355584u, 2283596308329535809u }, { 13920024512871794791u, 1826877046663628647u }, { 3757321980813615186u, 1461501637330902918u }, { 10384555214134712795u, 1169201309864722334u }, { 5547241898389809503u, 1870722095783555735u }, { 4437793518711847602u, 1496577676626844588u }, { 10928932444453298728u, 1197262141301475670u }, { 17486291911125277965u, 1915619426082361072u }, { 6610335899416401726u, 1532495540865888858u }, { 12666966349016942027u, 1225996432692711086u }, { 12888448528943286597u, 1961594292308337738u }, { 17689456452638449924u, 1569275433846670190u }, { 14151565162110759939u, 1255420347077336152u }, { 7885109000409574610u, 2008672555323737844u }, { 9997436015069570011u, 1606938044258990275u }, { 7997948812055656009u, 1285550435407192220u }, { 12796718099289049614u, 2056880696651507552u }, { 2858676849947419045u, 1645504557321206042u }, { 13354987924183666206u, 1316403645856964833u }, { 17678631863951955605u, 2106245833371143733u }, { 3074859046935833515u, 1684996666696914987u }, { 13527933681774397782u, 1347997333357531989u }, { 10576647446613305481u, 2156795733372051183u }, { 15840015586774465031u, 1725436586697640946u }, { 8982663654677661702u, 1380349269358112757u }, { 18061610662226169046u, 2208558830972980411u }, { 10759939715039024913u, 1766847064778384329u }, { 12297300586773130254u, 1413477651822707463u }, { 15986332124095098083u, 2261564242916331941u }, { 9099716884534168143u, 1809251394333065553u }, { 14658471137111155161u, 1447401115466452442u }, { 4348079280205103483u, 1157920892373161954u }, { 14335624477811986218u, 1852673427797059126u }, { 7779150767507678651u, 1482138742237647301u }, { 2533971799264232598u, 1185710993790117841u }, { 15122401323048503126u, 1897137590064188545u }, { 12097921058438802501u, 1517710072051350836u }, { 5988988032009131678u, 1214168057641080669u }, { 16961078480698431330u, 1942668892225729070u }, { 13568862784558745064u, 1554135113780583256u }, { 7165741412905085728u, 1243308091024466605u }, { 11465186260648137165u, 1989292945639146568u }, { 16550846638002330379u, 1591434356511317254u }, { 16930026125143774626u, 1273147485209053803u }, { 4951948911778577463u, 2037035976334486086u }, { 272210314680951647u, 1629628781067588869u }, { 3907117066486671641u, 1303703024854071095u }, { 6251387306378674625u, 2085924839766513752u }, { 16069156289328670670u, 1668739871813211001u }, { 9165976216721026213u, 1334991897450568801u }, { 7286864317269821294u, 2135987035920910082u }, { 16897537898041588005u, 1708789628736728065u }, { 13518030318433270404u, 1367031702989382452u }, { 6871453250525591353u, 2187250724783011924u }, { 9186511415162383406u, 1749800579826409539u }, { 11038557946871817048u, 1399840463861127631u }, { 10282995085511086630u, 2239744742177804210u }, { 8226396068408869304u, 1791795793742243368u }, { 13959814484210916090u, 1433436634993794694u }, { 11267656730511734774u, 2293498615990071511u }, { 5324776569667477496u, 1834798892792057209u }, { 7949170070475892320u, 1467839114233645767u }, { 17427382500606444826u, 1174271291386916613u }, { 5747719112518849781u, 1878834066219066582u }, { 15666221734240810795u, 1503067252975253265u }, { 12532977387392648636u, 1202453802380202612u }, { 5295368560860596524u, 1923926083808324180u }, { 4236294848688477220u, 1539140867046659344u }, { 7078384693692692099u, 1231312693637327475u }, { 11325415509908307358u, 1970100309819723960u }, { 9060332407926645887u, 1576080247855779168u }, { 14626963555825137356u, 1260864198284623334u }, { 12335095245094488799u, 2017382717255397335u }, { 9868076196075591040u, 1613906173804317868u }, { 15273158586344293478u, 1291124939043454294u }, { 13369007293925138595u, 2065799902469526871u }, { 7005857020398200553u, 1652639921975621497u }, { 16672732060544291412u, 1322111937580497197u }, { 11918976037903224966u, 2115379100128795516u }, { 5845832015580669650u, 1692303280103036413u }, { 12055363241948356366u, 1353842624082429130u }, { 841837113407818570u, 2166148198531886609u }, { 4362818505468165179u, 1732918558825509287u }, { 14558301248600263113u, 1386334847060407429u }, { 12225235553534690011u, 2218135755296651887u }, { 2401490813343931363u, 1774508604237321510u }, { 1921192650675145090u, 1419606883389857208u }, { 17831303500047873437u, 2271371013423771532u }, { 6886345170554478103u, 1817096810739017226u }, { 1819727321701672159u, 1453677448591213781u }, { 16213177116328979020u, 1162941958872971024u }, { 14873036941900635463u, 1860707134196753639u }, { 15587778368262418694u, 1488565707357402911u }, { 8780873879868024632u, 1190852565885922329u }, { 2981351763563108441u, 1905364105417475727u }, { 13453127855076217722u, 1524291284333980581u }, { 7073153469319063855u, 1219433027467184465u }, { 11317045550910502167u, 1951092843947495144u }, { 12742985255470312057u, 1560874275157996115u }, { 10194388204376249646u, 1248699420126396892u }, { 1553625868034358140u, 1997919072202235028u }, { 8621598323911307159u, 1598335257761788022u }, { 17965325103354776697u, 1278668206209430417u }, { 13987124906400001422u, 2045869129935088668u }, { 121653480894270168u, 1636695303948070935u }, { 97322784715416134u, 1309356243158456748u }, { 14913111714512307107u, 2094969989053530796u }, { 8241140556867935363u, 1675975991242824637u }, { 17660958889720079260u, 1340780792994259709u }, { 17189487779326395846u, 2145249268790815535u }, { 13751590223461116677u, 1716199415032652428u }, { 18379969808252713988u, 1372959532026121942u }, { 14650556434236701088u, 2196735251241795108u }, { 652398703163629901u, 1757388200993436087u }, { 11589965406756634890u, 1405910560794748869u }, { 7475898206584884855u, 2249456897271598191u }, { 2291369750525997561u, 1799565517817278553u }, { 9211793429904618695u, 1439652414253822842u }, { 18428218302589300235u, 2303443862806116547u }, { 7363877012587619542u, 1842755090244893238u }, { 13269799239553916280u, 1474204072195914590u }, { 10615839391643133024u, 1179363257756731672u }, { 2227947767661371545u, 1886981212410770676u }, { 16539753473096738529u, 1509584969928616540u }, { 13231802778477390823u, 1207667975942893232u }, { 6413489186596184024u, 1932268761508629172u }, { 16198837793502678189u, 1545815009206903337u }, { 5580372605318321905u, 1236652007365522670u }, { 8928596168509315048u, 1978643211784836272u }, { 18210923379033183008u, 1582914569427869017u }, { 7190041073742725760u, 1266331655542295214u }, { 436019273762630246u, 2026130648867672343u }, { 7727513048493924843u, 1620904519094137874u }, { 9871359253537050198u, 1296723615275310299u }, { 4726128361433549347u, 2074757784440496479u }, { 7470251503888749801u, 1659806227552397183u }, { 13354898832594820487u, 1327844982041917746u }, { 13989140502667892133u, 2124551971267068394u }, { 14880661216876224029u, 1699641577013654715u }, { 11904528973500979224u, 1359713261610923772u }, { 4289851098633925465u, 2175541218577478036u }, { 18189276137874781665u, 1740432974861982428u }, { 3483374466074094362u, 1392346379889585943u }, { 1884050330976640656u, 2227754207823337509u }, { 5196589079523222848u, 1782203366258670007u }, { 15225317707844309248u, 1425762693006936005u }, { 5913764258841343181u, 2281220308811097609u }, { 8420360221814984868u, 1824976247048878087u }, { 17804334621677718864u, 1459980997639102469u }, { 17932816512084085415u, 1167984798111281975u }, { 10245762345624985047u, 1868775676978051161u }, { 4507261061758077715u, 1495020541582440929u }, { 7295157664148372495u, 1196016433265952743u }, { 7982903447895485668u, 1913626293225524389u }, { 10075671573058298858u, 1530901034580419511u }, { 4371188443704728763u, 1224720827664335609u }, { 14372599139411386667u, 1959553324262936974u }, { 15187428126271019657u, 1567642659410349579u }, { 15839291315758726049u, 1254114127528279663u }, { 3206773216762499739u, 2006582604045247462u }, { 13633465017635730761u, 1605266083236197969u }, { 14596120828850494932u, 1284212866588958375u }, { 4907049252451240275u, 2054740586542333401u }, { 236290587219081897u, 1643792469233866721u }, { 14946427728742906810u, 1315033975387093376u }, { 16535586736504830250u, 2104054360619349402u }, { 5849771759720043554u, 1683243488495479522u }, { 15747863852001765813u, 1346594790796383617u }, { 10439186904235184007u, 2154551665274213788u }, { 15730047152871967852u, 1723641332219371030u }, { 12584037722297574282u, 1378913065775496824u }, { 9066413911450387881u, 2206260905240794919u }, { 10942479943902220628u, 1765008724192635935u }, { 8753983955121776503u, 1412006979354108748u }, { 10317025513452932081u, 2259211166966573997u }, { 874922781278525018u, 1807368933573259198u }, { 8078635854506640661u, 1445895146858607358u }, { 13841606313089133175u, 1156716117486885886u }, { 14767872471458792434u, 1850745787979017418u }, { 746251532941302978u, 1480596630383213935u }, { 597001226353042382u, 1184477304306571148u }, { 15712597221132509104u, 1895163686890513836u }, { 8880728962164096960u, 1516130949512411069u }, { 10793931984473187891u, 1212904759609928855u }, { 17270291175157100626u, 1940647615375886168u }, { 2748186495899949531u, 1552518092300708935u }, { 2198549196719959625u, 1242014473840567148u }, { 18275073973719576693u, 1987223158144907436u }, { 10930710364233751031u, 1589778526515925949u }, { 12433917106128911148u, 1271822821212740759u }, { 8826220925580526867u, 2034916513940385215u }, { 7060976740464421494u, 1627933211152308172u }, { 16716827836597268165u, 1302346568921846537u }, { 11989529279587987770u, 2083754510274954460u }, { 9591623423670390216u, 1667003608219963568u }, { 15051996368420132820u, 1333602886575970854u }, { 13015147745246481542u, 2133764618521553367u }, { 3033420566713364587u, 1707011694817242694u }, { 6116085268112601993u, 1365609355853794155u }, { 9785736428980163188u, 2184974969366070648u }, { 15207286772667951197u, 1747979975492856518u }, { 1097782973908629988u, 1398383980394285215u }, { 1756452758253807981u, 2237414368630856344u }, { 5094511021344956708u, 1789931494904685075u }, { 4075608817075965366u, 1431945195923748060u }, { 6520974107321544586u, 2291112313477996896u }, { 1527430471115325346u, 1832889850782397517u }, { 12289990821117991246u, 1466311880625918013u }, { 17210690286378213644u, 1173049504500734410u }, { 9090360384495590213u, 1876879207201175057u }, { 18340334751822203140u, 1501503365760940045u }, { 14672267801457762512u, 1201202692608752036u }, { 16096930852848599373u, 1921924308174003258u }, { 1809498238053148529u, 1537539446539202607u }, { 12515645034668249793u, 1230031557231362085u }, { 1578287981759648052u, 1968050491570179337u }, { 12330676829633449412u, 1574440393256143469u }, { 13553890278448669853u, 1259552314604914775u }, { 3239480371808320148u, 2015283703367863641u }, { 17348979556414297411u, 1612226962694290912u }, { 6500486015647617283u, 1289781570155432730u }, { 10400777625036187652u, 2063650512248692368u }, { 15699319729512770768u, 1650920409798953894u }, { 16248804598352126938u, 1320736327839163115u }, { 7551343283653851484u, 2113178124542660985u }, { 6041074626923081187u, 1690542499634128788u }, { 12211557331022285596u, 1352433999707303030u }, { 1091747655926105338u, 2163894399531684849u }, { 4562746939482794594u, 1731115519625347879u }, { 7339546366328145998u, 1384892415700278303u }, { 8053925371383123274u, 2215827865120445285u }, { 6443140297106498619u, 1772662292096356228u }, { 12533209867169019542u, 1418129833677084982u }, { 5295740528502789974u, 2269007733883335972u }, { 15304638867027962949u, 1815206187106668777u }, { 4865013464138549713u, 1452164949685335022u }, { 14960057215536570740u, 1161731959748268017u }, { 9178696285890871890u, 1858771135597228828u }, { 14721654658196518159u, 1487016908477783062u }, { 4398626097073393881u, 1189613526782226450u }, { 7037801755317430209u, 1903381642851562320u }, { 5630241404253944167u, 1522705314281249856u }, { 814844308661245011u, 1218164251424999885u }, { 1303750893857992017u, 1949062802279999816u }, { 15800395974054034906u, 1559250241823999852u }, { 5261619149759407279u, 1247400193459199882u }, { 12107939454356961969u, 1995840309534719811u }, { 5997002748743659252u, 1596672247627775849u }, { 8486951013736837725u, 1277337798102220679u }, { 2511075177753209390u, 2043740476963553087u }, { 13076906586428298482u, 1634992381570842469u }, { 14150874083884549109u, 1307993905256673975u }, { 4194654460505726958u, 2092790248410678361u }, { 18113118827372222859u, 1674232198728542688u }, { 3422448617672047318u, 1339385758982834151u }, { 16543964232501006678u, 2143017214372534641u }, { 9545822571258895019u, 1714413771498027713u }, { 15015355686490936662u, 1371531017198422170u }, { 5577825024675947042u, 2194449627517475473u }, { 11840957649224578280u, 1755559702013980378u }, { 16851463748863483271u, 1404447761611184302u }, { 12204946739213931940u, 2247116418577894884u }, { 13453306206113055875u, 1797693134862315907u }, { 3383947335406624054u, 1438154507889852726u }, { 16482362180876329456u, 2301047212623764361u }, { 9496540929959153242u, 1840837770099011489u }, { 11286581558709232917u, 1472670216079209191u }, { 5339916432225476010u, 1178136172863367353u }, { 4854517476818851293u, 1885017876581387765u }, { 3883613981455081034u, 1508014301265110212u }, { 14174937629389795797u, 1206411441012088169u }, { 11611853762797942306u, 1930258305619341071u }, { 5600134195496443521u, 1544206644495472857u }, { 15548153800622885787u, 1235365315596378285u }, { 6430302007287065643u, 1976584504954205257u }, { 16212288050055383484u, 1581267603963364205u }, { 12969830440044306787u, 1265014083170691364u }, { 9683682259845159889u, 2024022533073106183u }, { 15125643437359948558u, 1619218026458484946u }, { 8411165935146048523u, 1295374421166787957u }, { 17147214310975587960u, 2072599073866860731u }, { 10028422634038560045u, 1658079259093488585u }, { 8022738107230848036u, 1326463407274790868u }, { 9147032156827446534u, 2122341451639665389u }, { 11006974540203867551u, 1697873161311732311u }, { 5116230817421183718u, 1358298529049385849u }, { 15564666937357714594u, 2173277646479017358u }, { 1383687105660440706u, 1738622117183213887u }, { 12174996128754083534u, 1390897693746571109u }, { 8411947361780802685u, 2225436309994513775u }, { 6729557889424642148u, 1780349047995611020u }, { 5383646311539713719u, 1424279238396488816u }, { 1235136468979721303u, 2278846781434382106u }, { 15745504434151418335u, 1823077425147505684u }, { 16285752362063044992u, 1458461940118004547u }, { 5649904260166615347u, 1166769552094403638u }, { 5350498001524674232u, 1866831283351045821u }, { 591049586477829062u, 1493465026680836657u }, { 11540886113407994219u, 1194772021344669325u }, { 18673707743239135u, 1911635234151470921u }, { 14772334225162232601u, 1529308187321176736u }, { 8128518565387875758u, 1223446549856941389u }, { 1937583260394870242u, 1957514479771106223u }, { 8928764237799716840u, 1566011583816884978u }, { 14521709019723594119u, 1252809267053507982u }, { 8477339172590109297u, 2004494827285612772u }, { 17849917782297818407u, 1603595861828490217u }, { 6901236596354434079u, 1282876689462792174u }, { 18420676183650915173u, 2052602703140467478u }, { 3668494502695001169u, 1642082162512373983u }, { 10313493231639821582u, 1313665730009899186u }, { 9122891541139893884u, 2101865168015838698u }, { 14677010862395735754u, 1681492134412670958u }, { 673562245690857633u, 1345193707530136767u } }; static const uint64_t DOUBLE_POW5_SPLIT[DOUBLE_POW5_TABLE_SIZE][2] = { { 0u, 1152921504606846976u }, { 0u, 1441151880758558720u }, { 0u, 1801439850948198400u }, { 0u, 2251799813685248000u }, { 0u, 1407374883553280000u }, { 0u, 1759218604441600000u }, { 0u, 2199023255552000000u }, { 0u, 1374389534720000000u }, { 0u, 1717986918400000000u }, { 0u, 2147483648000000000u }, { 0u, 1342177280000000000u }, { 0u, 1677721600000000000u }, { 0u, 2097152000000000000u }, { 0u, 1310720000000000000u }, { 0u, 1638400000000000000u }, { 0u, 2048000000000000000u }, { 0u, 1280000000000000000u }, { 0u, 1600000000000000000u }, { 0u, 2000000000000000000u }, { 0u, 1250000000000000000u }, { 0u, 1562500000000000000u }, { 0u, 1953125000000000000u }, { 0u, 1220703125000000000u }, { 0u, 1525878906250000000u }, { 0u, 1907348632812500000u }, { 0u, 1192092895507812500u }, { 0u, 1490116119384765625u }, { 4611686018427387904u, 1862645149230957031u }, { 9799832789158199296u, 1164153218269348144u }, { 12249790986447749120u, 1455191522836685180u }, { 15312238733059686400u, 1818989403545856475u }, { 14528612397897220096u, 2273736754432320594u }, { 13692068767113150464u, 1421085471520200371u }, { 12503399940464050176u, 1776356839400250464u }, { 15629249925580062720u, 2220446049250313080u }, { 9768281203487539200u, 1387778780781445675u }, { 7598665485932036096u, 1734723475976807094u }, { 274959820560269312u, 2168404344971008868u }, { 9395221924704944128u, 1355252715606880542u }, { 2520655369026404352u, 1694065894508600678u }, { 12374191248137781248u, 2117582368135750847u }, { 14651398557727195136u, 1323488980084844279u }, { 13702562178731606016u, 1654361225106055349u }, { 3293144668132343808u, 2067951531382569187u }, { 18199116482078572544u, 1292469707114105741u }, { 8913837547316051968u, 1615587133892632177u }, { 15753982952572452864u, 2019483917365790221u }, { 12152082354571476992u, 1262177448353618888u }, { 15190102943214346240u, 1577721810442023610u }, { 9764256642163156992u, 1972152263052529513u }, { 17631875447420442880u, 1232595164407830945u }, { 8204786253993389888u, 1540743955509788682u }, { 1032610780636961552u, 1925929944387235853u }, { 2951224747111794922u, 1203706215242022408u }, { 3689030933889743652u, 1504632769052528010u }, { 13834660704216955373u, 1880790961315660012u }, { 17870034976990372916u, 1175494350822287507u }, { 17725857702810578241u, 1469367938527859384u }, { 3710578054803671186u, 1836709923159824231u }, { 26536550077201078u, 2295887403949780289u }, { 11545800389866720434u, 1434929627468612680u }, { 14432250487333400542u, 1793662034335765850u }, { 8816941072311974870u, 2242077542919707313u }, { 17039803216263454053u, 1401298464324817070u }, { 12076381983474541759u, 1751623080406021338u }, { 5872105442488401391u, 2189528850507526673u }, { 15199280947623720629u, 1368455531567204170u }, { 9775729147674874978u, 1710569414459005213u }, { 16831347453020981627u, 2138211768073756516u }, { 1296220121283337709u, 1336382355046097823u }, { 15455333206886335848u, 1670477943807622278u }, { 10095794471753144002u, 2088097429759527848u }, { 6309871544845715001u, 1305060893599704905u }, { 12499025449484531656u, 1631326116999631131u }, { 11012095793428276666u, 2039157646249538914u }, { 11494245889320060820u, 1274473528905961821u }, { 532749306367912313u, 1593091911132452277u }, { 5277622651387278295u, 1991364888915565346u }, { 7910200175544436838u, 1244603055572228341u }, { 14499436237857933952u, 1555753819465285426u }, { 8900923260467641632u, 1944692274331606783u }, { 12480606065433357876u, 1215432671457254239u }, { 10989071563364309441u, 1519290839321567799u }, { 9124653435777998898u, 1899113549151959749u }, { 8008751406574943263u, 1186945968219974843u }, { 5399253239791291175u, 1483682460274968554u }, { 15972438586593889776u, 1854603075343710692u }, { 759402079766405302u, 1159126922089819183u }, { 14784310654990170340u, 1448908652612273978u }, { 9257016281882937117u, 1811135815765342473u }, { 16182956370781059300u, 2263919769706678091u }, { 7808504722524468110u, 1414949856066673807u }, { 5148944884728197234u, 1768687320083342259u }, { 1824495087482858639u, 2210859150104177824u }, { 1140309429676786649u, 1381786968815111140u }, { 1425386787095983311u, 1727233711018888925u }, { 6393419502297367043u, 2159042138773611156u }, { 13219259225790630210u, 1349401336733506972u }, { 16524074032238287762u, 1686751670916883715u }, { 16043406521870471799u, 2108439588646104644u }, { 803757039314269066u, 1317774742903815403u }, { 14839754354425000045u, 1647218428629769253u }, { 4714634887749086344u, 2059023035787211567u }, { 9864175832484260821u, 1286889397367007229u }, { 16941905809032713930u, 1608611746708759036u }, { 2730638187581340797u, 2010764683385948796u }, { 10930020904093113806u, 1256727927116217997u }, { 18274212148543780162u, 1570909908895272496u }, { 4396021111970173586u, 1963637386119090621u }, { 5053356204195052443u, 1227273366324431638u }, { 15540067292098591362u, 1534091707905539547u }, { 14813398096695851299u, 1917614634881924434u }, { 13870059828862294966u, 1198509146801202771u }, { 12725888767650480803u, 1498136433501503464u }, { 15907360959563101004u, 1872670541876879330u }, { 14553786618154326031u, 1170419088673049581u }, { 4357175217410743827u, 1463023860841311977u }, { 10058155040190817688u, 1828779826051639971u }, { 7961007781811134206u, 2285974782564549964u }, { 14199001900486734687u, 1428734239102843727u }, { 13137066357181030455u, 1785917798878554659u }, { 11809646928048900164u, 2232397248598193324u }, { 16604401366885338411u, 1395248280373870827u }, { 16143815690179285109u, 1744060350467338534u }, { 10956397575869330579u, 2180075438084173168u }, { 6847748484918331612u, 1362547148802608230u }, { 17783057643002690323u, 1703183936003260287u }, { 17617136035325974999u, 2128979920004075359u }, { 17928239049719816230u, 1330612450002547099u }, { 17798612793722382384u, 1663265562503183874u }, { 13024893955298202172u, 2079081953128979843u }, { 5834715712847682405u, 1299426220705612402u }, { 16516766677914378815u, 1624282775882015502u }, { 11422586310538197711u, 2030353469852519378u }, { 11750802462513761473u, 1268970918657824611u }, { 10076817059714813937u, 1586213648322280764u }, { 12596021324643517422u, 1982767060402850955u }, { 5566670318688504437u, 1239229412751781847u }, { 2346651879933242642u, 1549036765939727309u }, { 7545000868343941206u, 1936295957424659136u }, { 4715625542714963254u, 1210184973390411960u }, { 5894531928393704067u, 1512731216738014950u }, { 16591536947346905892u, 1890914020922518687u }, { 17287239619732898039u, 1181821263076574179u }, { 16997363506238734644u, 1477276578845717724u }, { 2799960309088866689u, 1846595723557147156u }, { 10973347230035317489u, 1154122327223216972u }, { 13716684037544146861u, 1442652909029021215u }, { 12534169028502795672u, 1803316136286276519u }, { 11056025267201106687u, 2254145170357845649u }, { 18439230838069161439u, 1408840731473653530u }, { 13825666510731675991u, 1761050914342066913u }, { 3447025083132431277u, 2201313642927583642u }, { 6766076695385157452u, 1375821026829739776u }, { 8457595869231446815u, 1719776283537174720u }, { 10571994836539308519u, 2149720354421468400u }, { 6607496772837067824u, 1343575221513417750u }, { 17482743002901110588u, 1679469026891772187u }, { 17241742735199000331u, 2099336283614715234u }, { 15387775227926763111u, 1312085177259197021u }, { 5399660979626290177u, 1640106471573996277u }, { 11361262242960250625u, 2050133089467495346u }, { 11712474920277544544u, 1281333180917184591u }, { 10028907631919542777u, 1601666476146480739u }, { 7924448521472040567u, 2002083095183100924u }, { 14176152362774801162u, 1251301934489438077u }, { 3885132398186337741u, 1564127418111797597u }, { 9468101516160310080u, 1955159272639746996u }, { 15140935484454969608u, 1221974545399841872u }, { 479425281859160394u, 1527468181749802341u }, { 5210967620751338397u, 1909335227187252926u }, { 17091912818251750210u, 1193334516992033078u }, { 12141518985959911954u, 1491668146240041348u }, { 15176898732449889943u, 1864585182800051685u }, { 11791404716994875166u, 1165365739250032303u }, { 10127569877816206054u, 1456707174062540379u }, { 8047776328842869663u, 1820883967578175474u }, { 836348374198811271u, 2276104959472719343u }, { 7440246761515338900u, 1422565599670449589u }, { 13911994470321561530u, 1778206999588061986u }, { 8166621051047176104u, 2222758749485077483u }, { 2798295147690791113u, 1389224218428173427u }, { 17332926989895652603u, 1736530273035216783u }, { 17054472718942177850u, 2170662841294020979u }, { 8353202440125167204u, 1356664275808763112u }, { 10441503050156459005u, 1695830344760953890u }, { 3828506775840797949u, 2119787930951192363u }, { 86973725686804766u, 1324867456844495227u }, { 13943775212390669669u, 1656084321055619033u }, { 3594660960206173375u, 2070105401319523792u }, { 2246663100128858359u, 1293815875824702370u }, { 12031700912015848757u, 1617269844780877962u }, { 5816254103165035138u, 2021587305976097453u }, { 5941001823691840913u, 1263492066235060908u }, { 7426252279614801142u, 1579365082793826135u }, { 4671129331091113523u, 1974206353492282669u }, { 5225298841145639904u, 1233878970932676668u }, { 6531623551432049880u, 1542348713665845835u }, { 3552843420862674446u, 1927935892082307294u }, { 16055585193321335241u, 1204959932551442058u }, { 10846109454796893243u, 1506199915689302573u }, { 18169322836923504458u, 1882749894611628216u }, { 11355826773077190286u, 1176718684132267635u }, { 9583097447919099954u, 1470898355165334544u }, { 11978871809898874942u, 1838622943956668180u }, { 14973589762373593678u, 2298278679945835225u }, { 2440964573842414192u, 1436424174966147016u }, { 3051205717303017741u, 1795530218707683770u }, { 13037379183483547984u, 2244412773384604712u }, { 8148361989677217490u, 1402757983365377945u }, { 14797138505523909766u, 1753447479206722431u }, { 13884737113477499304u, 2191809349008403039u }, { 15595489723564518921u, 1369880843130251899u }, { 14882676136028260747u, 1712351053912814874u }, { 9379973133180550126u, 2140438817391018593u }, { 17391698254306313589u, 1337774260869386620u }, { 3292878744173340370u, 1672217826086733276u }, { 4116098430216675462u, 2090272282608416595u }, { 266718509671728212u, 1306420176630260372u }, { 333398137089660265u, 1633025220787825465u }, { 5028433689789463235u, 2041281525984781831u }, { 10060300083759496378u, 1275800953740488644u }, { 12575375104699370472u, 1594751192175610805u }, { 1884160825592049379u, 1993438990219513507u }, { 17318501580490888525u, 1245899368887195941u }, { 7813068920331446945u, 1557374211108994927u }, { 5154650131986920777u, 1946717763886243659u }, { 915813323278131534u, 1216698602428902287u }, { 14979824709379828129u, 1520873253036127858u }, { 9501408849870009354u, 1901091566295159823u }, { 12855909558809837702u, 1188182228934474889u }, { 2234828893230133415u, 1485227786168093612u }, { 2793536116537666769u, 1856534732710117015u }, { 8663489100477123587u, 1160334207943823134u }, { 1605989338741628675u, 1450417759929778918u }, { 11230858710281811652u, 1813022199912223647u }, { 9426887369424876662u, 2266277749890279559u }, { 12809333633531629769u, 1416423593681424724u }, { 16011667041914537212u, 1770529492101780905u }, { 6179525747111007803u, 2213161865127226132u }, { 13085575628799155685u, 1383226165704516332u }, { 16356969535998944606u, 1729032707130645415u }, { 15834525901571292854u, 2161290883913306769u }, { 2979049660840976177u, 1350806802445816731u }, { 17558870131333383934u, 1688508503057270913u }, { 8113529608884566205u, 2110635628821588642u }, { 9682642023980241782u, 1319147268013492901u }, { 16714988548402690132u, 1648934085016866126u }, { 11670363648648586857u, 2061167606271082658u }, { 11905663298832754689u, 1288229753919426661u }, { 1047021068258779650u, 1610287192399283327u }, { 15143834390605638274u, 2012858990499104158u }, { 4853210475701136017u, 1258036869061940099u }, { 1454827076199032118u, 1572546086327425124u }, { 1818533845248790147u, 1965682607909281405u }, { 3442426662494187794u, 1228551629943300878u }, { 13526405364972510550u, 1535689537429126097u }, { 3072948650933474476u, 1919611921786407622u }, { 15755650962115585259u, 1199757451116504763u }, { 15082877684217093670u, 1499696813895630954u }, { 9630225068416591280u, 1874621017369538693u }, { 8324733676974063502u, 1171638135855961683u }, { 5794231077790191473u, 1464547669819952104u }, { 7242788847237739342u, 1830684587274940130u }, { 18276858095901949986u, 2288355734093675162u }, { 16034722328366106645u, 1430222333808546976u }, { 1596658836748081690u, 1787777917260683721u }, { 6607509564362490017u, 2234722396575854651u }, { 1823850468512862308u, 1396701497859909157u }, { 6891499104068465790u, 1745876872324886446u }, { 17837745916940358045u, 2182346090406108057u }, { 4231062170446641922u, 1363966306503817536u }, { 5288827713058302403u, 1704957883129771920u }, { 6611034641322878003u, 2131197353912214900u }, { 13355268687681574560u, 1331998346195134312u }, { 16694085859601968200u, 1664997932743917890u }, { 11644235287647684442u, 2081247415929897363u }, { 4971804045566108824u, 1300779634956185852u }, { 6214755056957636030u, 1625974543695232315u }, { 3156757802769657134u, 2032468179619040394u }, { 6584659645158423613u, 1270292612261900246u }, { 17454196593302805324u, 1587865765327375307u }, { 17206059723201118751u, 1984832206659219134u }, { 6142101308573311315u, 1240520129162011959u }, { 3065940617289251240u, 1550650161452514949u }, { 8444111790038951954u, 1938312701815643686u }, { 665883850346957067u, 1211445438634777304u }, { 832354812933696334u, 1514306798293471630u }, { 10263815553021896226u, 1892883497866839537u }, { 17944099766707154901u, 1183052186166774710u }, { 13206752671529167818u, 1478815232708468388u }, { 16508440839411459773u, 1848519040885585485u }, { 12623618533845856310u, 1155324400553490928u }, { 15779523167307320387u, 1444155500691863660u }, { 1277659885424598868u, 1805194375864829576u }, { 1597074856780748586u, 2256492969831036970u }, { 5609857803915355770u, 1410308106144398106u }, { 16235694291748970521u, 1762885132680497632u }, { 1847873790976661535u, 2203606415850622041u }, { 12684136165428883219u, 1377254009906638775u }, { 11243484188358716120u, 1721567512383298469u }, { 219297180166231438u, 2151959390479123087u }, { 7054589765244976505u, 1344974619049451929u }, { 13429923224983608535u, 1681218273811814911u }, { 12175718012802122765u, 2101522842264768639u }, { 14527352785642408584u, 1313451776415480399u }, { 13547504963625622826u, 1641814720519350499u }, { 12322695186104640628u, 2052268400649188124u }, { 16925056528170176201u, 1282667750405742577u }, { 7321262604930556539u, 1603334688007178222u }, { 18374950293017971482u, 2004168360008972777u }, { 4566814905495150320u, 1252605225005607986u }, { 14931890668723713708u, 1565756531257009982u }, { 9441491299049866327u, 1957195664071262478u }, { 1289246043478778550u, 1223247290044539049u }, { 6223243572775861092u, 1529059112555673811u }, { 3167368447542438461u, 1911323890694592264u }, { 1979605279714024038u, 1194577431684120165u }, { 7086192618069917952u, 1493221789605150206u }, { 18081112809442173248u, 1866527237006437757u }, { 13606538515115052232u, 1166579523129023598u }, { 7784801107039039482u, 1458224403911279498u }, { 507629346944023544u, 1822780504889099373u }, { 5246222702107417334u, 2278475631111374216u }, { 3278889188817135834u, 1424047269444608885u }, { 8710297504448807696u, 1780059086805761106u } }; #define DOUBLE_MANTISSA_BITS 52 #define DOUBLE_EXPONENT_BITS 11 #define DOUBLE_BIAS 1023 static inline uint32_t decimalLength17(const uint64_t v) { // This is slightly faster than a loop. // The average output length is 16.38 digits, so we check high-to-low. // Function precondition: v is not an 18, 19, or 20-digit number. // (17 digits are sufficient for round-tripping.) assert(v < 100000000000000000L); if (v >= 10000000000000000L) { return 17; } if (v >= 1000000000000000L) { return 16; } if (v >= 100000000000000L) { return 15; } if (v >= 10000000000000L) { return 14; } if (v >= 1000000000000L) { return 13; } if (v >= 100000000000L) { return 12; } if (v >= 10000000000L) { return 11; } if (v >= 1000000000L) { return 10; } if (v >= 100000000L) { return 9; } if (v >= 10000000L) { return 8; } if (v >= 1000000L) { return 7; } if (v >= 100000L) { return 6; } if (v >= 10000L) { return 5; } if (v >= 1000L) { return 4; } if (v >= 100L) { return 3; } if (v >= 10L) { return 2; } return 1; } // A floating decimal representing m * 10^e. typedef struct floating_decimal_64 { uint64_t mantissa; // Decimal exponent's range is -324 to 308 // inclusive, and can fit in a short if needed. int32_t exponent; } floating_decimal_64; static inline floating_decimal_64 d2d(const uint64_t ieeeMantissa, const uint32_t ieeeExponent) { int32_t e2; uint64_t m2; if (ieeeExponent == 0) { // We subtract 2 so that the bounds computation has 2 additional bits. e2 = 1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS - 2; m2 = ieeeMantissa; } else { e2 = (int32_t) ieeeExponent - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS - 2; m2 = (1ull << DOUBLE_MANTISSA_BITS) | ieeeMantissa; } const bool even = (m2 & 1) == 0; const bool acceptBounds = even; // Step 2: Determine the interval of valid decimal representations. const uint64_t mv = 4 * m2; // Implicit bool -> int conversion. True is 1, false is 0. const uint32_t mmShift = ieeeMantissa != 0 || ieeeExponent <= 1; // We would compute mp and mm like this: // uint64_t mp = 4 * m2 + 2; // uint64_t mm = mv - 1 - mmShift; // Step 3: Convert to a decimal power base using 128-bit arithmetic. uint64_t vr, vp, vm; int32_t e10; bool vmIsTrailingZeros = false; bool vrIsTrailingZeros = false; if (e2 >= 0) { // I tried special-casing q == 0, but there was no effect on // performance. // This expression is slightly faster than max(0, log10Pow2(e2) - 1). const uint32_t q = log10Pow2(e2) - (e2 > 3); e10 = (int32_t) q; const int32_t k = DOUBLE_POW5_INV_BITCOUNT + pow5bits((int32_t) q) - 1; const int32_t i = -e2 + (int32_t) q + k; vr = mulShiftAll64(m2, DOUBLE_POW5_INV_SPLIT[q], i, &vp, &vm, mmShift); if (q <= 21) { // This should use q <= 22, but I think 21 is also safe. Smaller // values may still be safe, but it's more difficult to reason // about them. Only one of mp, mv, and mm can be a multiple of 5, // if any. const uint32_t mvMod5 = ((uint32_t) mv) - 5 * ((uint32_t) div5(mv)); if (mvMod5 == 0) { vrIsTrailingZeros = multipleOfPowerOf5(mv, q); } else if (acceptBounds) { // Same as min(e2 + (~mm & 1), pow5Factor(mm)) >= q // <=> e2 + (~mm & 1) >= q && pow5Factor(mm) >= q // <=> true && pow5Factor(mm) >= q, since e2 >= q. vmIsTrailingZeros = multipleOfPowerOf5(mv - 1 - mmShift, q); } else { // Same as min(e2 + 1, pow5Factor(mp)) >= q. vp -= multipleOfPowerOf5(mv + 2, q); } } } else { // This expression is slightly faster than max(0, log10Pow5(-e2) - 1). const uint32_t q = log10Pow5(-e2) - (-e2 > 1); e10 = (int32_t) q + e2; const int32_t i = -e2 - (int32_t) q; const int32_t k = pow5bits(i) - DOUBLE_POW5_BITCOUNT; const int32_t j = (int32_t) q - k; vr = mulShiftAll64(m2, DOUBLE_POW5_SPLIT[i], j, &vp, &vm, mmShift); if (q <= 1) { // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q // trailing 0 bits. mv = 4 * m2, so it always has at least two // trailing 0 bits. vrIsTrailingZeros = true; if (acceptBounds) { // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff // mmShift == 1. vmIsTrailingZeros = mmShift == 1; } else { // mp = mv + 2, so it always has at least one trailing 0 bit. --vp; } } else if (q < 63) { // TODO(ulfjack): Use a tighter bound here. // We want to know if the full product has at least q trailing // zeros. // We need to compute min(p2(mv), p5(mv) - e2) >= q // <=> p2(mv) >= q && p5(mv) - e2 >= q // <=> p2(mv) >= q (because -e2 >= q) vrIsTrailingZeros = multipleOfPowerOf2(mv, q); } } // Step 4: Find the shortest decimal representation in the interval of // valid representations. int32_t removed = 0; uint8_t lastRemovedDigit = 0; uint64_t output; // On average, we remove ~2 digits. if (vmIsTrailingZeros || vrIsTrailingZeros) { // General case, which happens rarely (~0.7%). for (;;) { const uint64_t vpDiv10 = div10(vp); const uint64_t vmDiv10 = div10(vm); if (vpDiv10 <= vmDiv10) { break; } const uint32_t vmMod10 = ((uint32_t) vm) - 10 * ((uint32_t) vmDiv10); const uint64_t vrDiv10 = div10(vr); const uint32_t vrMod10 = ((uint32_t) vr) - 10 * ((uint32_t) vrDiv10); vmIsTrailingZeros &= vmMod10 == 0; vrIsTrailingZeros &= lastRemovedDigit == 0; lastRemovedDigit = (uint8_t) vrMod10; vr = vrDiv10; vp = vpDiv10; vm = vmDiv10; ++removed; } if (vmIsTrailingZeros) { for (;;) { const uint64_t vmDiv10 = div10(vm); const uint32_t vmMod10 = ((uint32_t) vm) - 10 * ((uint32_t) vmDiv10); if (vmMod10 != 0) { break; } const uint64_t vpDiv10 = div10(vp); const uint64_t vrDiv10 = div10(vr); const uint32_t vrMod10 = ((uint32_t) vr) - 10 * ((uint32_t) vrDiv10); vrIsTrailingZeros &= lastRemovedDigit == 0; lastRemovedDigit = (uint8_t) vrMod10; vr = vrDiv10; vp = vpDiv10; vm = vmDiv10; ++removed; } } if (vrIsTrailingZeros && lastRemovedDigit == 5 && vr % 2 == 0) { // Round even if the exact number is .....50..0. lastRemovedDigit = 4; } // We need to take vr + 1 if vr is outside bounds or we need to round // up. output = vr + ((vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || lastRemovedDigit >= 5); } else { // Specialized for the common case (~99.3%). Percentages below are // relative to this. bool roundUp = false; const uint64_t vpDiv100 = div100(vp); const uint64_t vmDiv100 = div100(vm); if (vpDiv100 > vmDiv100) { // Opt: remove two digits at a time (~86.2%). const uint64_t vrDiv100 = div100(vr); const uint32_t vrMod100 = ((uint32_t) vr) - 100 * ((uint32_t) vrDiv100); roundUp = vrMod100 >= 50; vr = vrDiv100; vp = vpDiv100; vm = vmDiv100; removed += 2; } // Loop iterations below (approximately), without optimization above: // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02% // Loop iterations below (approximately), with optimization above: // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02% for (;;) { const uint64_t vpDiv10 = div10(vp); const uint64_t vmDiv10 = div10(vm); if (vpDiv10 <= vmDiv10) { break; } const uint64_t vrDiv10 = div10(vr); const uint32_t vrMod10 = ((uint32_t) vr) - 10 * ((uint32_t) vrDiv10); roundUp = vrMod10 >= 5; vr = vrDiv10; vp = vpDiv10; vm = vmDiv10; ++removed; } // We need to take vr + 1 if vr is outside bounds or we need to round // up. output = vr + (vr == vm || roundUp); } const int32_t exp = e10 + removed; floating_decimal_64 fd; fd.exponent = exp; fd.mantissa = output; return fd; } static inline int to_chars64(const floating_decimal_64 v, const bool sign, char* const result) { // Step 5: Print the decimal representation. int index = 0; if (sign) { result[index++] = '-'; } uint64_t output = v.mantissa; const uint32_t olength = decimalLength17(output); // Print the decimal digits. // The following code is equivalent to: // for (uint32_t i = 0; i < olength - 1; ++i) { // const uint32_t c = output % 10; output /= 10; // result[index + olength - i] = (char) ('0' + c); // } // result[index] = '0' + output % 10; uint32_t i = 0; // We prefer 32-bit operations, even on 64-bit platforms. // We have at most 17 digits, and uint32_t can store 9 digits. // If output doesn't fit into uint32_t, we cut off 8 digits, // so the rest will fit into uint32_t. if ((output >> 32) != 0) { // Expensive 64-bit division. const uint64_t q = div1e8(output); uint32_t output2 = ((uint32_t) output) - 100000000 * ((uint32_t) q); output = q; const uint32_t c = output2 % 10000; output2 /= 10000; const uint32_t d = output2 % 10000; const uint32_t c0 = (c % 100) << 1; const uint32_t c1 = (c / 100) << 1; const uint32_t d0 = (d % 100) << 1; const uint32_t d1 = (d / 100) << 1; memcpy(result + index + olength - 1, DIGIT_TABLE + c0, 2); memcpy(result + index + olength - 3, DIGIT_TABLE + c1, 2); memcpy(result + index + olength - 5, DIGIT_TABLE + d0, 2); memcpy(result + index + olength - 7, DIGIT_TABLE + d1, 2); i += 8; } uint32_t output2 = (uint32_t) output; while (output2 >= 10000) { #ifdef __clang__ // https://bugs.llvm.org/show_bug.cgi?id=38217 const uint32_t c = output2 - 10000 * (output2 / 10000); #else const uint32_t c = output2 % 10000; #endif output2 /= 10000; const uint32_t c0 = (c % 100) << 1; const uint32_t c1 = (c / 100) << 1; memcpy(result + index + olength - i - 1, DIGIT_TABLE + c0, 2); memcpy(result + index + olength - i - 3, DIGIT_TABLE + c1, 2); i += 4; } if (output2 >= 100) { const uint32_t c = (output2 % 100) << 1; output2 /= 100; memcpy(result + index + olength - i - 1, DIGIT_TABLE + c, 2); i += 2; } if (output2 >= 10) { const uint32_t c = output2 << 1; // We can't use memcpy here: the decimal dot goes between these two // digits. result[index + olength - i] = DIGIT_TABLE[c + 1]; result[index] = DIGIT_TABLE[c]; } else { result[index] = (char) ('0' + output2); } // Print decimal point if needed. if (olength > 1) { result[index + 1] = '.'; index += olength + 1; } else { ++index; } // Print the exponent. result[index++] = 'E'; int32_t exp = v.exponent + (int32_t) olength - 1; if (exp < 0) { result[index++] = '-'; exp = -exp; } if (exp >= 100) { const int32_t c = exp % 10; memcpy(result + index, DIGIT_TABLE + 2 * (exp / 10), 2); result[index + 2] = (char) ('0' + c); index += 3; } else if (exp >= 10) { memcpy(result + index, DIGIT_TABLE + 2 * exp, 2); index += 2; } else { result[index++] = (char) ('0' + exp); } return index; } static inline bool d2d_small_int(const uint64_t ieeeMantissa, const uint32_t ieeeExponent, floating_decimal_64* const v) { const uint64_t m2 = (1ull << DOUBLE_MANTISSA_BITS) | ieeeMantissa; const int32_t e2 = (int32_t) ieeeExponent - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS; if (e2 > 0) { // f = m2 * 2^e2 >= 2^53 is an integer. // Ignore this case for now. return false; } if (e2 < -52) { // f < 1. return false; } // Since 2^52 <= m2 < 2^53 and 0 <= -e2 <= 52: 1 <= f = m2 / 2^-e2 < 2^53. // Test if the lower -e2 bits of the significand are 0, i.e. whether the // fraction is 0. const uint64_t mask = (1ull << -e2) - 1; const uint64_t fraction = m2 & mask; if (fraction != 0) { return false; } // f is an integer in the range [1, 2^53). // Note: mantissa might contain trailing (decimal) 0's. // Note: since 2^53 < 10^16, there is no need to adjust decimalLength17(). v->mantissa = m2 >> -e2; v->exponent = 0; return true; } static int d2s_buffered_n(double f, char* result) { // Step 1: Decode the floating-point number, and unify normalized and // subnormal cases. const uint64_t bits = double_to_bits(f); // Decode bits into sign, mantissa, and exponent. const bool ieeeSign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0; const uint64_t ieeeMantissa = bits & ((1ull << DOUBLE_MANTISSA_BITS) - 1); const uint32_t ieeeExponent = (uint32_t) ((bits >> DOUBLE_MANTISSA_BITS) & ((1u << DOUBLE_EXPONENT_BITS) - 1)); // Case distinction; exit early for the easy cases. if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u) || (ieeeExponent == 0 && ieeeMantissa == 0)) { return copy_special_str(result, ieeeSign, ieeeExponent, ieeeMantissa); } floating_decimal_64 v; const bool isSmallInt = d2d_small_int(ieeeMantissa, ieeeExponent, &v); if (isSmallInt) { // For small integers in the range [1, 2^53), v.mantissa might contain // trailing (decimal) zeros. // For scientific notation we need to move these zeros into the // exponent. // (This is not needed for fixed-point notation, so it might be // beneficial to trim // trailing zeros in to_chars only if needed - once fixed-point // notation output is implemented.) for (;;) { const uint64_t q = div10(v.mantissa); const uint32_t r = ((uint32_t) v.mantissa) - 10 * ((uint32_t) q); if (r != 0) { break; } v.mantissa = q; ++v.exponent; } } else { v = d2d(ieeeMantissa, ieeeExponent); } return to_chars64(v, ieeeSign, result); } static void d2s_buffered(double f, char* result) { (void)umul128; (void)shiftright128; (void)mod1e9; const int index = d2s_buffered_n(f, result); // Terminate the string. result[index] = '\0'; } // Returns the number of decimal digits in v, which must not contain more than // 9 digits. static inline uint32_t decimalLength9(const uint32_t v) { // Function precondition: v is not a 10-digit number. // (f2s: 9 digits are sufficient for round-tripping.) // (d2fixed: We print 9-digit blocks.) assert(v < 1000000000); if (v >= 100000000) { return 9; } if (v >= 10000000) { return 8; } if (v >= 1000000) { return 7; } if (v >= 100000) { return 6; } if (v >= 10000) { return 5; } if (v >= 1000) { return 4; } if (v >= 100) { return 3; } if (v >= 10) { return 2; } return 1; } static inline uint32_t float_to_bits(const float f) { uint32_t bits = 0; memcpy(&bits, &f, sizeof(float)); return bits; } #define DOUBLE_POW5_INV_BITCOUNT 125 #define DOUBLE_POW5_BITCOUNT 125 #define DOUBLE_POW5_INV_TABLE_SIZE 342 #define DOUBLE_POW5_TABLE_SIZE 326 #define FLOAT_POW5_INV_BITCOUNT (DOUBLE_POW5_INV_BITCOUNT - 64) #define FLOAT_POW5_BITCOUNT (DOUBLE_POW5_BITCOUNT - 64) static inline uint32_t pow5factor_32(uint32_t value) { uint32_t count = 0; for (;;) { assert(value != 0); const uint32_t q = value / 5; const uint32_t r = value % 5; if (r != 0) { break; } value = q; ++count; } return count; } // Returns true if value is divisible by 5^p. static inline bool multipleOfPowerOf5_32(const uint32_t value, const uint32_t p) { return pow5factor_32(value) >= p; } // Returns true if value is divisible by 2^p. static inline bool multipleOfPowerOf2_32(const uint32_t value, const uint32_t p) { // __builtin_ctz doesn't appear to be faster here. return (value & ((1u << p) - 1)) == 0; } // It seems to be slightly faster to avoid uint128_t here, although the // generated code for uint128_t looks slightly nicer. static inline uint32_t mulShift32(const uint32_t m, const uint64_t factor, const int32_t shift) { assert(shift > 32); // The casts here help MSVC to avoid calls to the __allmul library // function. const uint32_t factorLo = (uint32_t)(factor); const uint32_t factorHi = (uint32_t)(factor >> 32); const uint64_t bits0 = (uint64_t)m * factorLo; const uint64_t bits1 = (uint64_t)m * factorHi; const uint64_t sum = (bits0 >> 32) + bits1; const uint64_t shiftedSum = sum >> (shift - 32); assert(shiftedSum <= UINT32_MAX); return (uint32_t) shiftedSum; } static inline uint32_t mulPow5InvDivPow2(const uint32_t m, const uint32_t q, const int32_t j) { return mulShift32(m, DOUBLE_POW5_INV_SPLIT[q][1] + 1, j); } static inline uint32_t mulPow5divPow2(const uint32_t m, const uint32_t i, const int32_t j) { return mulShift32(m, DOUBLE_POW5_SPLIT[i][1], j); } #define FLOAT_MANTISSA_BITS 23 #define FLOAT_EXPONENT_BITS 8 #define FLOAT_BIAS 127 // A floating decimal representing m * 10^e. typedef struct floating_decimal_32 { uint32_t mantissa; // Decimal exponent's range is -45 to 38 // inclusive, and can fit in a short if needed. int32_t exponent; } floating_decimal_32; static inline floating_decimal_32 f2d(const uint32_t ieeeMantissa, const uint32_t ieeeExponent) { int32_t e2; uint32_t m2; if (ieeeExponent == 0) { // We subtract 2 so that the bounds computation has 2 additional bits. e2 = 1 - FLOAT_BIAS - FLOAT_MANTISSA_BITS - 2; m2 = ieeeMantissa; } else { e2 = (int32_t) ieeeExponent - FLOAT_BIAS - FLOAT_MANTISSA_BITS - 2; m2 = (1u << FLOAT_MANTISSA_BITS) | ieeeMantissa; } const bool even = (m2 & 1) == 0; const bool acceptBounds = even; // Step 2: Determine the interval of valid decimal representations. const uint32_t mv = 4 * m2; const uint32_t mp = 4 * m2 + 2; // Implicit bool -> int conversion. True is 1, false is 0. const uint32_t mmShift = ieeeMantissa != 0 || ieeeExponent <= 1; const uint32_t mm = 4 * m2 - 1 - mmShift; // Step 3: Convert to a decimal power base using 64-bit arithmetic. uint32_t vr, vp, vm; int32_t e10; bool vmIsTrailingZeros = false; bool vrIsTrailingZeros = false; uint8_t lastRemovedDigit = 0; if (e2 >= 0) { const uint32_t q = log10Pow2(e2); e10 = (int32_t) q; const int32_t k = FLOAT_POW5_INV_BITCOUNT + pow5bits((int32_t) q) - 1; const int32_t i = -e2 + (int32_t) q + k; vr = mulPow5InvDivPow2(mv, q, i); vp = mulPow5InvDivPow2(mp, q, i); vm = mulPow5InvDivPow2(mm, q, i); if (q != 0 && (vp - 1) / 10 <= vm / 10) { // We need to know one removed digit even if we are not going to // loop below. We could use q = X - 1 above, except that would // require 33 bits for the result, and we've found that // 32-bit arithmetic is faster even on 64-bit machines. const int32_t l = FLOAT_POW5_INV_BITCOUNT + pow5bits((int32_t)(q - 1)) - 1; lastRemovedDigit = (uint8_t) (mulPow5InvDivPow2(mv, q - 1, -e2 + (int32_t) q - 1 + l) % 10); } if (q <= 9) { // The largest power of 5 that fits in 24 bits is 5^10, but q <= 9 // seems to be safe as well. // Only one of mp, mv, and mm can be a multiple of 5, if any. if (mv % 5 == 0) { vrIsTrailingZeros = multipleOfPowerOf5_32(mv, q); } else if (acceptBounds) { vmIsTrailingZeros = multipleOfPowerOf5_32(mm, q); } else { vp -= multipleOfPowerOf5_32(mp, q); } } } else { const uint32_t q = log10Pow5(-e2); e10 = (int32_t) q + e2; const int32_t i = -e2 - (int32_t) q; const int32_t k = pow5bits(i) - FLOAT_POW5_BITCOUNT; int32_t j = (int32_t) q - k; vr = mulPow5divPow2(mv, (uint32_t) i, j); vp = mulPow5divPow2(mp, (uint32_t) i, j); vm = mulPow5divPow2(mm, (uint32_t) i, j); if (q != 0 && (vp - 1) / 10 <= vm / 10) { j = (int32_t) q - 1 - (pow5bits(i + 1) - FLOAT_POW5_BITCOUNT); lastRemovedDigit = (uint8_t) (mulPow5divPow2(mv, (uint32_t) (i + 1), j) % 10); } if (q <= 1) { // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q // trailing 0 bits. // mv = 4 * m2, so it always has at least two trailing 0 bits. vrIsTrailingZeros = true; if (acceptBounds) { // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff // mmShift == 1. vmIsTrailingZeros = mmShift == 1; } else { // mp = mv + 2, so it always has at least one trailing 0 bit. --vp; } } else if (q < 31) { // TODO(ulfjack): Use a tighter bound here. vrIsTrailingZeros = multipleOfPowerOf2_32(mv, q - 1); } } // Step 4: Find the shortest decimal representation in the interval of // valid representations. int32_t removed = 0; uint32_t output; if (vmIsTrailingZeros || vrIsTrailingZeros) { // General case, which happens rarely (~4.0%). while (vp / 10 > vm / 10) { #ifdef __clang__ // https://bugs.llvm.org/show_bug.cgi?id=23106 // The compiler does not realize that vm % 10 can be computed from // vm / 10 as vm - (vm / 10) * 10. vmIsTrailingZeros &= vm - (vm / 10) * 10 == 0; #else vmIsTrailingZeros &= vm % 10 == 0; #endif vrIsTrailingZeros &= lastRemovedDigit == 0; lastRemovedDigit = (uint8_t) (vr % 10); vr /= 10; vp /= 10; vm /= 10; ++removed; } if (vmIsTrailingZeros) { while (vm % 10 == 0) { vrIsTrailingZeros &= lastRemovedDigit == 0; lastRemovedDigit = (uint8_t) (vr % 10); vr /= 10; vp /= 10; vm /= 10; ++removed; } } if (vrIsTrailingZeros && lastRemovedDigit == 5 && vr % 2 == 0) { // Round even if the exact number is .....50..0. lastRemovedDigit = 4; } // We need to take vr + 1 if vr is outside bounds or we need to round // up. output = vr + ((vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || lastRemovedDigit >= 5); } else { // Specialized for the common case (~96.0%). Percentages below are // relative to this. // Loop iterations below (approximately): // 0: 13.6%, 1: 70.7%, 2: 14.1%, 3: 1.39%, 4: 0.14%, 5+: 0.01% while (vp / 10 > vm / 10) { lastRemovedDigit = (uint8_t) (vr % 10); vr /= 10; vp /= 10; vm /= 10; ++removed; } // We need to take vr + 1 if vr is outside bounds or we need to round // up. output = vr + (vr == vm || lastRemovedDigit >= 5); } const int32_t exp = e10 + removed; floating_decimal_32 fd; fd.exponent = exp; fd.mantissa = output; return fd; } static inline int to_chars(const floating_decimal_32 v, const bool sign, char* const result) { // Step 5: Print the decimal representation. int index = 0; if (sign) { result[index++] = '-'; } uint32_t output = v.mantissa; const uint32_t olength = decimalLength9(output); // Print the decimal digits. // The following code is equivalent to: // for (uint32_t i = 0; i < olength - 1; ++i) { // const uint32_t c = output % 10; output /= 10; // result[index + olength - i] = (char) ('0' + c); // } // result[index] = '0' + output % 10; uint32_t i = 0; while (output >= 10000) { #ifdef __clang__ // https://bugs.llvm.org/show_bug.cgi?id=38217 const uint32_t c = output - 10000 * (output / 10000); #else const uint32_t c = output % 10000; #endif output /= 10000; const uint32_t c0 = (c % 100) << 1; const uint32_t c1 = (c / 100) << 1; memcpy(result + index + olength - i - 1, DIGIT_TABLE + c0, 2); memcpy(result + index + olength - i - 3, DIGIT_TABLE + c1, 2); i += 4; } if (output >= 100) { const uint32_t c = (output % 100) << 1; output /= 100; memcpy(result + index + olength - i - 1, DIGIT_TABLE + c, 2); i += 2; } if (output >= 10) { const uint32_t c = output << 1; // We can't use memcpy here: the decimal dot goes between these two // digits. result[index + olength - i] = DIGIT_TABLE[c + 1]; result[index] = DIGIT_TABLE[c]; } else { result[index] = (char) ('0' + output); } // Print decimal point if needed. if (olength > 1) { result[index + 1] = '.'; index += olength + 1; } else { ++index; } // Print the exponent. result[index++] = 'E'; int32_t exp = v.exponent + (int32_t) olength - 1; if (exp < 0) { result[index++] = '-'; exp = -exp; } if (exp >= 10) { memcpy(result + index, DIGIT_TABLE + 2 * exp, 2); index += 2; } else { result[index++] = (char) ('0' + exp); } return index; } static int f2s_buffered_n(float f, char* result) { // Step 1: Decode the floating-point number, and unify normalized and // subnormal cases. const uint32_t bits = float_to_bits(f); // Decode bits into sign, mantissa, and exponent. const bool ieeeSign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0; const uint32_t ieeeMantissa = bits & ((1u << FLOAT_MANTISSA_BITS) - 1); const uint32_t ieeeExponent = (bits >> FLOAT_MANTISSA_BITS) & ((1u << FLOAT_EXPONENT_BITS) - 1); // Case distinction; exit early for the easy cases. if (ieeeExponent == ((1u << FLOAT_EXPONENT_BITS) - 1u) || (ieeeExponent == 0 && ieeeMantissa == 0)) { return copy_special_str(result, ieeeSign, ieeeExponent, ieeeMantissa); } const floating_decimal_32 v = f2d(ieeeMantissa, ieeeExponent); return to_chars(v, ieeeSign, result); } static void f2s_buffered(float f, char* result) { const int index = f2s_buffered_n(f, result); // Terminate the string. result[index] = '\0'; } enum RyuStatus { RYU_SUCCESS, RYU_INPUT_TOO_SHORT, RYU_INPUT_TOO_LONG, RYU_MALFORMED_INPUT }; // Returns e == 0 ? 1 : [log_2(5^e)]; requires 0 <= e <= 3528. static inline int32_t log2pow5(const int32_t e) { // This approximation works up to the point that the multiplication // overflows at e = 3529. // If the multiplication were done in 64 bits, it would fail at 5^4004 // which is just greater than 2^9297. assert(e >= 0); assert(e <= 3528); return (int32_t) ((((uint32_t) e) * 1217359) >> 19); } // Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 3528. static inline int32_t ceil_log2pow5(const int32_t e) { return log2pow5(e) + 1; } // These tables are generated by PrintDoubleLookupTable. #define DOUBLE_POW5_INV_BITCOUNT 125 #define DOUBLE_POW5_BITCOUNT 125 #define DOUBLE_POW5_INV_TABLE_SIZE 342 #define DOUBLE_POW5_TABLE_SIZE 326 #define DOUBLE_MANTISSA_BITS 52 #define DOUBLE_EXPONENT_BITS 11 #define DOUBLE_EXPONENT_BIAS 1023 static inline uint32_t floor_log2(const uint64_t value) { return 63 - __builtin_clzll(value); } // The max function is already defined on Windows. static inline int32_t max32(int32_t a, int32_t b) { return a < b ? b : a; } static inline double int64Bits2Double(uint64_t bits) { double f; memcpy(&f, &bits, sizeof(double)); return f; } static enum RyuStatus s2d_n(const char * buffer, const int len, double * result) { if (len == 0) { return RYU_INPUT_TOO_SHORT; } int m10digits = 0; int e10digits = 0; int dotIndex = len; int eIndex = len; uint64_t m10 = 0; int32_t e10 = 0; bool signedM = false; bool signedE = false; int i = 0; if (buffer[i] == '-') { signedM = true; i++; } for (; i < len; i++) { char c = buffer[i]; if (c == '.') { if (dotIndex != len) { return RYU_MALFORMED_INPUT; } dotIndex = i; continue; } if ((c < '0') || (c > '9')) { break; } if (m10digits >= 17) { return RYU_INPUT_TOO_LONG; } m10 = 10 * m10 + (c - '0'); if (m10 != 0) { m10digits++; } } if (i < len && ((buffer[i] == 'e') || (buffer[i] == 'E'))) { eIndex = i; i++; if (i < len && ((buffer[i] == '-') || (buffer[i] == '+'))) { signedE = buffer[i] == '-'; i++; } for (; i < len; i++) { char c = buffer[i]; if ((c < '0') || (c > '9')) { return RYU_MALFORMED_INPUT; } if (e10digits > 3) { // TODO: Be more lenient. Return +/-Infinity or +/-0 instead. return RYU_INPUT_TOO_LONG; } e10 = 10 * e10 + (c - '0'); if (e10 != 0) { e10digits++; } } } if (i < len) { return RYU_MALFORMED_INPUT; } if (signedE) { e10 = -e10; } e10 -= dotIndex < eIndex ? eIndex - dotIndex - 1 : 0; if (m10 == 0) { *result = signedM ? -0.0 : 0.0; return RYU_SUCCESS; } if ((m10digits + e10 <= -324) || (m10 == 0)) { // Number is less than 1e-324, which should be rounded down to 0; // return +/-0.0. uint64_t ieee = ((uint64_t) signedM) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS); *result = int64Bits2Double(ieee); return RYU_SUCCESS; } if (m10digits + e10 >= 310) { // Number is larger than 1e+309, which should be rounded to +/-Infinity. uint64_t ieee = (((uint64_t) signedM) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS)) | (0x7ffull << DOUBLE_MANTISSA_BITS); *result = int64Bits2Double(ieee); return RYU_SUCCESS; } // Convert to binary float m2 * 2^e2, while retaining information about // whether the conversion was exact (trailingZeros). int32_t e2; uint64_t m2; bool trailingZeros; if (e10 >= 0) { // The length of m * 10^e in bits is: // log2(m10 * 10^e10) = log2(m10) + e10 log2(10) = log2(m10) + e10 + // e10 * log2(5) // // We want to compute the DOUBLE_MANTISSA_BITS + 1 top-most bits (+1 // for the implicit leading one in IEEE format). We therefore choose a // binary output exponent of // log2(m10 * 10^e10) - (DOUBLE_MANTISSA_BITS + 1). // // We use floor(log2(5^e10)) so that we get at least this many bits; // better to have an additional bit than to not have enough bits. e2 = floor_log2(m10) + e10 + log2pow5(e10) - (DOUBLE_MANTISSA_BITS + 1); // We now compute [m10 * 10^e10 / 2^e2] = [m10 * 5^e10 / 2^(e2-e10)]. // To that end, we use the DOUBLE_POW5_SPLIT table. int j = e2 - e10 - ceil_log2pow5(e10) + DOUBLE_POW5_BITCOUNT; assert(j >= 0); assert(e10 < DOUBLE_POW5_TABLE_SIZE); m2 = mulShift64(m10, DOUBLE_POW5_SPLIT[e10], j); // We also compute if the result is exact, i.e., // [m10 * 10^e10 / 2^e2] == m10 * 10^e10 / 2^e2. // This can only be the case if 2^e2 divides m10 * 10^e10, which in // turn requires that the largest power of 2 that divides m10 + e10 is // greater than e2. If e2 is less than e10, then the result must be // exact. Otherwise we use the existing multipleOfPowerOf2 function. trailingZeros = e2 < e10 || (e2 - e10 < 64 && multipleOfPowerOf2(m10, e2 - e10)); } else { e2 = floor_log2(m10) + e10 - ceil_log2pow5(-e10) - (DOUBLE_MANTISSA_BITS + 1); int j = e2 - e10 + ceil_log2pow5(-e10) - 1 + DOUBLE_POW5_INV_BITCOUNT; assert(-e10 < DOUBLE_POW5_INV_TABLE_SIZE); m2 = mulShift64(m10, DOUBLE_POW5_INV_SPLIT[-e10], j); trailingZeros = multipleOfPowerOf5(m10, -e10); } // Compute the final IEEE exponent. uint32_t ieee_e2 = (uint32_t) max32(0, e2 + DOUBLE_EXPONENT_BIAS + floor_log2(m2)); if (ieee_e2 > 0x7fe) { // Final IEEE exponent is larger than the maximum representable; // return +/-Infinity. uint64_t ieee = (((uint64_t) signedM) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS)) | (0x7ffull << DOUBLE_MANTISSA_BITS); *result = int64Bits2Double(ieee); return RYU_SUCCESS; } // We need to figure out how much we need to shift m2. The tricky part is // that we need to take the final IEEE exponent into account, so we need to // reverse the bias and also special-case the value 0. int32_t shift = (ieee_e2 == 0 ? 1 : ieee_e2) - e2 - DOUBLE_EXPONENT_BIAS - DOUBLE_MANTISSA_BITS; assert(shift >= 0); // We need to round up if the exact value is more than 0.5 above the value // we computed. That's equivalent to checking if the last removed bit was 1 // and either the value was not just trailing zeros or the result would // otherwise be odd. // // We need to update trailingZeros given that we have the exact output // exponent ieee_e2 now. trailingZeros &= (m2 & ((1ull << (shift - 1)) - 1)) == 0; uint64_t lastRemovedBit = (m2 >> (shift - 1)) & 1; bool roundUp = (lastRemovedBit != 0) && (!trailingZeros || (((m2 >> shift) & 1) != 0)); uint64_t ieee_m2 = (m2 >> shift) + roundUp; assert(ieee_m2 <= (1ull << (DOUBLE_MANTISSA_BITS + 1))); ieee_m2 &= (1ull << DOUBLE_MANTISSA_BITS) - 1; if (ieee_m2 == 0 && roundUp) { // Due to how the IEEE represents +/-Infinity, we don't need to check // for overflow here. ieee_e2++; } uint64_t ieee = (((((uint64_t) signedM) << DOUBLE_EXPONENT_BITS) | (uint64_t)ieee_e2) << DOUBLE_MANTISSA_BITS) | ieee_m2; *result = int64Bits2Double(ieee); return RYU_SUCCESS; } #define FLOAT_MANTISSA_BITS 23 #define FLOAT_EXPONENT_BITS 8 #define FLOAT_EXPONENT_BIAS 127 static inline uint32_t floor_log2_32(const uint32_t value) { return 31 - __builtin_clz(value); } static inline float int32Bits2Float(uint32_t bits) { float f; memcpy(&f, &bits, sizeof(float)); return f; } static enum RyuStatus s2f_n(const char * buffer, const int len, float * result){ if (len == 0) { return RYU_INPUT_TOO_SHORT; } int m10digits = 0; int e10digits = 0; int dotIndex = len; int eIndex = len; uint32_t m10 = 0; int32_t e10 = 0; bool signedM = false; bool signedE = false; int i = 0; if (buffer[i] == '-') { signedM = true; i++; } for (; i < len; i++) { char c = buffer[i]; if (c == '.') { if (dotIndex != len) { return RYU_MALFORMED_INPUT; } dotIndex = i; continue; } if ((c < '0') || (c > '9')) { break; } if (m10digits >= 9) { return RYU_INPUT_TOO_LONG; } m10 = 10 * m10 + (c - '0'); if (m10 != 0) { m10digits++; } } if (i < len && ((buffer[i] == 'e') || (buffer[i] == 'E'))) { eIndex = i; i++; if (i < len && ((buffer[i] == '-') || (buffer[i] == '+'))) { signedE = buffer[i] == '-'; i++; } for (; i < len; i++) { char c = buffer[i]; if ((c < '0') || (c > '9')) { return RYU_MALFORMED_INPUT; } if (e10digits > 3) { // TODO: Be more lenient. Return +/-Infinity or +/-0 instead. return RYU_INPUT_TOO_LONG; } e10 = 10 * e10 + (c - '0'); if (e10 != 0) { e10digits++; } } } if (i < len) { return RYU_MALFORMED_INPUT; } if (signedE) { e10 = -e10; } e10 -= dotIndex < eIndex ? eIndex - dotIndex - 1 : 0; if (m10 == 0) { *result = signedM ? -0.0f : 0.0f; return RYU_SUCCESS; } if ((m10digits + e10 <= -46) || (m10 == 0)) { // Number is less than 1e-46, which should be rounded down to 0; // return +/-0.0. uint32_t ieee = ((uint32_t) signedM) << (FLOAT_EXPONENT_BITS + FLOAT_MANTISSA_BITS); *result = int32Bits2Float(ieee); return RYU_SUCCESS; } if (m10digits + e10 >= 40) { // Number is larger than 1e+39, which should be rounded to +/-Infinity. uint32_t ieee = (((uint32_t) signedM) << (FLOAT_EXPONENT_BITS + FLOAT_MANTISSA_BITS)) | (0xffu << FLOAT_MANTISSA_BITS); *result = int32Bits2Float(ieee); return RYU_SUCCESS; } // Convert to binary float m2 * 2^e2, while retaining information about // whether the conversion was exact (trailingZeros). int32_t e2; uint32_t m2; bool trailingZeros; if (e10 >= 0) { // The length of m * 10^e in bits is: // log2(m10 * 10^e10) = log2(m10) + e10 log2(10) = log2(m10) + e10 + // e10 * log2(5) // // We want to compute the FLOAT_MANTISSA_BITS + 1 top-most bits (+1 for // the implicit leading one in IEEE format). We therefore choose a // binary output exponent of // log2(m10 * 10^e10) - (FLOAT_MANTISSA_BITS + 1). // // We use floor(log2(5^e10)) so that we get at least this many bits; // better to have an additional bit than to not have enough bits. e2 = floor_log2_32(m10) + e10 + log2pow5(e10) - (FLOAT_MANTISSA_BITS + 1); // We now compute [m10 * 10^e10 / 2^e2] = [m10 * 5^e10 / 2^(e2-e10)]. // To that end, we use the FLOAT_POW5_SPLIT table. int j = e2 - e10 - ceil_log2pow5(e10) + FLOAT_POW5_BITCOUNT; assert(j >= 0); m2 = mulPow5divPow2(m10, e10, j); // We also compute if the result is exact, i.e., // [m10 * 10^e10 / 2^e2] == m10 * 10^e10 / 2^e2. // This can only be the case if 2^e2 divides m10 * 10^e10, which in // turn requires that the largest power of 2 that divides m10 + e10 is // greater than e2. If e2 is less than e10, then the result must be // exact. Otherwise we use the existing multipleOfPowerOf2 function. trailingZeros = e2 < e10 || (e2 - e10 < 32 && multipleOfPowerOf2_32(m10, e2 - e10)); } else { e2 = floor_log2_32(m10) + e10 - ceil_log2pow5(-e10) - (FLOAT_MANTISSA_BITS + 1); // We now compute [m10 * 10^e10 / 2^e2] = [m10 / (5^(-e10) 2^(e2-e10))]. int j = e2 - e10 + ceil_log2pow5(-e10) - 1 + FLOAT_POW5_INV_BITCOUNT; m2 = mulPow5InvDivPow2(m10, -e10, j); // We also compute if the result is exact, i.e., // [m10 / (5^(-e10) 2^(e2-e10))] == m10 / (5^(-e10) 2^(e2-e10)) // // If e2-e10 >= 0, we need to check whether (5^(-e10) 2^(e2-e10)) // divides m10, which is the case iff pow5(m10) >= -e10 AND // pow2(m10) >= e2-e10. // // If e2-e10 < 0, we have actually computed // [m10 * 2^(e10 e2) / 5^(-e10)] above, and we need to check // whether 5^(-e10) divides (m10 * 2^(e10-e2)), which is the case iff // pow5(m10 * 2^(e10-e2)) = pow5(m10) >= -e10. trailingZeros = (e2 < e10 || (e2 - e10 < 32 && multipleOfPowerOf2_32(m10, e2 - e10))) && multipleOfPowerOf5_32(m10, -e10); } // Compute the final IEEE exponent. uint32_t ieee_e2 = (uint32_t) max32(0, e2 + FLOAT_EXPONENT_BIAS + floor_log2_32(m2)); if (ieee_e2 > 0xfe) { // Final IEEE exponent is larger than the maximum representable; // return +/-Infinity. uint32_t ieee = (((uint32_t) signedM) << (FLOAT_EXPONENT_BITS + FLOAT_MANTISSA_BITS)) | (0xffu << FLOAT_MANTISSA_BITS); *result = int32Bits2Float(ieee); return RYU_SUCCESS; } // We need to figure out how much we need to shift m2. The tricky part is // that we need to take the final IEEE exponent into account, so we need // to reverse the bias and also special-case the value 0. int32_t shift = (ieee_e2 == 0 ? 1 : ieee_e2) - e2 - FLOAT_EXPONENT_BIAS - FLOAT_MANTISSA_BITS; assert(shift >= 0); // We need to round up if the exact value is more than 0.5 above the value // we computed. That's equivalent to checking if the last removed bit was // 1 and either the value was not just trailing zeros or the result would // otherwise be odd. // // We need to update trailingZeros given that we have the exact output // exponent ieee_e2 now. trailingZeros &= (m2 & ((1u << (shift - 1)) - 1)) == 0; uint32_t lastRemovedBit = (m2 >> (shift - 1)) & 1; bool roundUp = (lastRemovedBit != 0) && (!trailingZeros || (((m2 >> shift) & 1) != 0)); uint32_t ieee_m2 = (m2 >> shift) + roundUp; assert(ieee_m2 <= (1u << (FLOAT_MANTISSA_BITS + 1))); ieee_m2 &= (1u << FLOAT_MANTISSA_BITS) - 1; if (ieee_m2 == 0 && roundUp) { // Rounding up may overflow the mantissa. // In this case we move a trailing zero of the mantissa into the // exponent. // Due to how the IEEE represents +/-Infinity, we don't need to check // for overflow here. ieee_e2++; } uint32_t ieee = (((((uint32_t) signedM) << FLOAT_EXPONENT_BITS) | (uint32_t)ieee_e2) << FLOAT_MANTISSA_BITS) | ieee_m2; *result = int32Bits2Float(ieee); return RYU_SUCCESS; } struct fp_writer { uint8_t *dst; size_t n; size_t count; }; static void fp_write_nullterm(struct fp_writer *wr) { if (wr->n > wr->count) wr->dst[wr->count] = '\0'; else if (wr->n > 0) wr->dst[wr->n-1] = '\0'; } static void fp_write_char(struct fp_writer *wr, char b) { if (wr->count < wr->n) wr->dst[wr->count] = b; wr->count++; } struct fp_info { bool ok; // number is valid bool sign; // has sign. Is a negative number size_t frac; // has dot. Index of '.' or zero if none size_t exp; // has exponent. Index of 'e' or zero if none size_t len; // number of bytes parsed }; /// Parses the next number from a data stream and returns information. /// This is compatible with JSON, WKT, and general numerical values. /// This does not convert the data into a number. /// It only get basic information for validation and later parsing. FP_EXTERN struct fp_info fp_parse(const char *data, size_t len) { size_t i = 0; bool sign = false; size_t frac = 0; size_t exp = 0; if (i == len) { goto fail; } // sign if (data[i] == '-') { sign = true; i++; if (i == len) { goto fail; } if (data[i] < '0' || data[i] > '9') { goto fail; } } // int if (i == len) { goto fail; } if (data[i] == '0') { i++; } else { for (; i < len; i++) { if (data[i] >= '0' && data[i] <= '9') { continue; } break; } } if (i == len) { goto ok; } // frac if (data[i] == '.') { frac = i; i++; if (i == len) { goto fail; } if (data[i] < '0' || data[i] > '9') { goto fail; } i++; for (; i < len; i++) { if (data[i] >= '0' && data[i] <= '9') { continue; } break; } } if (i == len) { goto ok; } // exp if (data[i] == 'e' || data[i] == 'E') { exp = i; i++; if (i == len) { goto fail; } if (data[i] == '+' || data[i] == '-') { i++; } if (i == len) { goto fail; } if (data[i] < '0' || data[i] > '9') { goto fail; } i++; for (; i < len; i++) { if (data[i] >= '0' && data[i] <= '9') { continue; } break; } } ok: return (struct fp_info){ true, sign, frac, exp, i }; fail: return (struct fp_info){ false, sign, frac, exp, i }; } union fpoint { double d; float f; }; static size_t fp_utoa(union fpoint fpoint, int bits, char fmt, char dst[], size_t nbytes) { struct fp_writer wr = { .dst = (uint8_t*)dst, .n = nbytes }; char buf[25]; bool f = true; bool g = false; bool j = false; char ech = 'e'; switch (fmt) { case 'j': case 'J': fmt -= 3; j = true; // fall through case 'g': case 'G': fmt -= 2; g = true; // fall through case 'e': case 'E': f = g; if (fmt == 'E') ech = 'E'; // fall through case 'f': if (bits == 32) { f2s_buffered(fpoint.f, buf); } else { d2s_buffered(fpoint.d, buf); } break; default: buf[0] = '\0'; } bool neg = false; char *p = buf; if (p[0] == '-') { fp_write_char(&wr, '-'); p++; neg = true; } char *end = p; char *e = NULL; while (*end) { if (*end >= 'E' && (*end == 'E' || *end == 'e')) { e = end; } end++; } if (!e) { if (*p == 'i' || *p == 'I') { strcpy(p, "Infinity"); } else if (*p == 'n' || *p == 'N') { strcpy(p, "NaN"); } else { *p = '\0'; } while (*p) fp_write_char(&wr, *(p++)); fp_write_nullterm(&wr); return wr.count; } if (!f) { *e = '\0'; while (*p) fp_write_char(&wr, *(p++)); fp_write_char(&wr, ech); p++; if (j && *p != '-') fp_write_char(&wr, '+'); while (*p) fp_write_char(&wr, *(p++)); fp_write_nullterm(&wr); return wr.count; } int en = atoi(e+1); *e = '\0'; if (en < 0) { fp_write_char(&wr, '0'); fp_write_char(&wr, '.'); en = -en; for (int i = 0; i < en-1; i++) { fp_write_char(&wr, '0'); } fp_write_char(&wr, *(p++)); if (*p) { p++; while (*p) fp_write_char(&wr, *(p++)); } } else { fp_write_char(&wr, *(p++)); if (*p) p++; for (int i = 0; i < en; i++) { if (*p) { fp_write_char(&wr, *(p++)); } else { fp_write_char(&wr, '0'); } } if (*p && !(*p == '0' && *(p+1) == '\0')) { fp_write_char(&wr, '.'); while (*p) fp_write_char(&wr, *(p++)); } } fp_write_nullterm(&wr); if (g) { bool rewrite = false; if (j) { rewrite = neg ? wr.count > 22 : wr.count > 21; } else { rewrite = (size_t)(end-buf) < wr.count; } if (rewrite) { // rewind and rewrite the buffer wr = (struct fp_writer){ .dst = (uint8_t*)dst, .n = nbytes }; p = buf; *e = '\0'; while (*p) fp_write_char(&wr, *(p++)); fp_write_char(&wr, ech); p++; if (j && *p != '-') fp_write_char(&wr, '+'); while (*p) fp_write_char(&wr, *(p++)); fp_write_nullterm(&wr); } } return wr.count; } /// fp_dtoa converts a double into a string representation that is copied /// into the provided C string buffer. /// /// Returns the number of characters, not including the null-terminator, needed /// to store the double into the C string buffer. /// If the returned length is greater than nbytes-1, then only a partial copy /// occurred. /// /// The format is one of /// 'e' (-d.ddddedd, a decimal exponent) /// 'E' (-d.ddddEdd, a decimal exponent) /// 'f' (-ddd.dddd, no exponent) /// 'g' ('e' for large exponents, 'f' otherwise) /// 'G' ('E' for large exponents, 'f' otherwise) /// 'j' ('e' for large exponents, 'f' otherwise) (matches javascript format) /// 'J' ('E' for large exponents, 'f' otherwise) (matches javascript format) FP_EXTERN size_t fp_dtoa(double d, char fmt, char dst[], size_t n) { return fp_utoa((union fpoint){ .d = d }, 64, fmt, dst, n); } /// fp_ftoa converts a float into a string representation that is copied /// into the provided C string buffer. /// /// Returns the number of characters, not including the null-terminator, needed /// to store the float into the C string buffer. /// If the returned length is greater than nbytes-1, then only a partial copy /// occurred. /// /// The format is one of /// 'e' (-d.ddddedd, a decimal exponent) /// 'E' (-d.ddddEdd, a decimal exponent) /// 'f' (-ddd.dddd, no exponent) /// 'g' ('e' for large exponents, 'f' otherwise) /// 'G' ('E' for large exponents, 'f' otherwise) /// 'j' ('e' for large exponents, 'f' otherwise) (matches javascript format) /// 'J' ('E' for large exponents, 'f' otherwise) (matches javascript format) FP_EXTERN size_t fp_ftoa(float f, char fmt, char dst[], size_t n) { return fp_utoa((union fpoint){ .f = f }, 32, fmt, dst, n); } // s2d_n is very strict and will sometime return RYU_INPUT_TOO_LONG even // though the number is correct, albeit just a very long string. // In that case fallback to the built-in strtod(). #define fp_fallback_clib(func) if (len > 0) { \ char *end; \ errno = 0; \ *x = func(data, &end); \ ok = (size_t)(end-data) == len && errno == 0; \ if (!ok) { \ *x = NAN; \ } \ } /// Convert a string to a double /// Returns false if the input is invalid FP_EXTERN bool fp_atod(const char *data, size_t len, double *x) { *x = NAN; bool ok = s2d_n(data, len, x) == RYU_SUCCESS; if (!ok) { fp_fallback_clib(strtod); } return ok; } /// Convert a string to a float /// Returns false if the input is invalid FP_EXTERN bool fp_atof(const char *data, size_t len, float *x) { *x = NAN; bool ok = s2f_n(data, len, x) == RYU_SUCCESS; if (!ok) { fp_fallback_clib(strtof); } return ok; } // END fp.c #if defined(__GNUC__) #pragma GCC diagnostic pop #endif #endif static void write_string_double(struct writer *wr, double f) { if (!isnormal(f)) { write_char(wr, '0'); return; } size_t dstsz = wr->count < wr->n ? wr->n - wr->count : 0; char *dst = wr->dst ? (char*)wr->dst+wr->count : 0; if (print_fixed_floats) { wr->count += fp_dtoa(f, 'f', dst, dstsz); } else { wr->count += fp_dtoa(f, 'g', dst, dstsz); } } static void write_posn_geojson(struct writer *wr, struct tg_point posn) { write_char(wr, '['); write_string_double(wr, posn.x); write_char(wr, ','); write_string_double(wr, posn.y); write_char(wr, ']'); } static void write_posn_geojson_3(struct writer *wr, struct tg_point posn, double z) { write_char(wr, '['); write_string_double(wr, posn.x); write_char(wr, ','); write_string_double(wr, posn.y); write_char(wr, ','); write_string_double(wr, z); write_char(wr, ']'); } static void write_posn_geojson_4(struct writer *wr, struct tg_point posn, double z, double m) { write_char(wr, '['); write_string_double(wr, posn.x); write_char(wr, ','); write_string_double(wr, posn.y); write_char(wr, ','); write_string_double(wr, z); write_char(wr, ','); write_string_double(wr, m); write_char(wr, ']'); } static int write_ring_points_geojson(struct writer *wr, const struct tg_ring *ring) { write_char(wr, '['); for (int i = 0 ; i < ring->npoints; i++) { if (i > 0) write_char(wr, ','); write_posn_geojson(wr, ring->points[i]); } write_char(wr, ']'); return ring->npoints; } static int write_ring_points_geojson_3(struct writer *wr, const struct tg_ring *ring, const double *coords, int ncoords) { double z; write_char(wr, '['); int j = 0; for (int i = 0 ; i < ring->npoints; i++) { if (i > 0) write_char(wr, ','); z = (j < ncoords) ? coords[j++] : 0; write_posn_geojson_3(wr, ring->points[i], z); } write_char(wr, ']'); return ring->npoints; } static int write_ring_points_geojson_4(struct writer *wr, const struct tg_ring *ring, const double *coords, int ncoords) { double z, m; write_char(wr, '['); int j = 0; for (int i = 0 ; i < ring->npoints; i++) { if (i > 0) write_char(wr, ','); z = (j < ncoords) ? coords[j++] : 0; m = (j < ncoords) ? coords[j++] : 0; write_posn_geojson_4(wr, ring->points[i], z, m); } write_char(wr, ']'); return ring->npoints; } static int write_poly_points_geojson(struct writer *wr, const struct tg_poly *poly) { int count = 0; write_char(wr, '['); write_ring_points_geojson(wr, tg_poly_exterior(poly)); int nholes = tg_poly_num_holes(poly); for (int i = 0 ; i < nholes; i++) { write_char(wr, ','); count += write_ring_points_geojson(wr, tg_poly_hole_at(poly, i)); } write_char(wr, ']'); return count; } static int write_poly_points_geojson_3(struct writer *wr, const struct tg_poly *poly, const double *coords, int ncoords) { int count = 0; double *pcoords = (double*)coords; write_char(wr, '['); const struct tg_ring *exterior = tg_poly_exterior(poly); int n = write_ring_points_geojson_3(wr, exterior, pcoords, ncoords); count += n; ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; int nholes = tg_poly_num_holes(poly); for (int i = 0 ; i < nholes; i++) { write_char(wr, ','); const struct tg_ring *hole = tg_poly_hole_at(poly, i); int n = write_ring_points_geojson_3(wr, hole, pcoords, ncoords); count += n; ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; } write_char(wr, ']'); return count; } static int write_poly_points_geojson_4(struct writer *wr, const struct tg_poly *poly, const double *coords, int ncoords) { int count = 0; double *pcoords = (double*)coords; write_char(wr, '['); const struct tg_ring *exterior = tg_poly_exterior(poly); int n = write_ring_points_geojson_4(wr, exterior, pcoords, ncoords); count += n; ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); int nholes = tg_poly_num_holes(poly); for (int i = 0 ; i < nholes; i++) { write_char(wr, ','); const struct tg_ring *hole = tg_poly_hole_at(poly, i); int n = write_ring_points_geojson_4(wr, hole, pcoords, ncoords); count += n; ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); } write_char(wr, ']'); return count; } static void write_geom_point_geojson(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "{\"type\":\"Point\",\"coordinates\":"); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_string(wr, "[]"); } else { if ((getflags(geom)&HAS_Z) == HAS_Z) { if ((getflags(geom)&HAS_M) == HAS_M) { write_posn_geojson_4(wr, geom->point, geom->z, geom->m); } else { write_posn_geojson_3(wr, geom->point, geom->z); } } else if ((getflags(geom)&HAS_M) == HAS_M) { write_posn_geojson_3(wr, geom->point, geom->m); } else { write_posn_geojson(wr, geom->point); } } write_char(wr, '}'); } static void write_geom_linestring_geojson(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "{\"type\":\"LineString\",\"coordinates\":"); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_string(wr, "[]"); } else { switch (tg_geom_dims(geom)) { case 3: write_ring_points_geojson_3(wr, (struct tg_ring*)geom->line, geom->coords, geom->ncoords); break; case 4: write_ring_points_geojson_4(wr, (struct tg_ring*)geom->line, geom->coords, geom->ncoords); break; default: write_ring_points_geojson(wr, (struct tg_ring*)geom->line); break; } } write_char(wr, '}'); } static void write_geom_polygon_geojson(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "{\"type\":\"Polygon\",\"coordinates\":"); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_string(wr, "[]"); } else { switch (tg_geom_dims(geom)) { case 3: write_poly_points_geojson_3(wr, geom->poly, geom->coords, geom->ncoords); break; case 4: write_poly_points_geojson_4(wr, geom->poly, geom->coords, geom->ncoords); break; default: // 2 write_poly_points_geojson(wr, geom->poly); break; } } write_char(wr, '}'); } static void write_geom_multipoint_geojson(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "{\"type\":\"MultiPoint\",\"coordinates\":["); if (geom->multi) { int dims = tg_geom_dims(geom); double z, m; double *coords = (double *)geom->coords; int ncoords = geom->ncoords; int j = 0; for (int i = 0; i < geom->multi->ngeoms; i++) { struct tg_point point = tg_geom_point(geom->multi->geoms[i]); if (i > 0) write_char(wr, ','); switch (dims) { case 3: z = (j < ncoords) ? coords[j++] : 0; write_posn_geojson_3(wr, point, z); break; case 4: z = (j < ncoords) ? coords[j++] : 0; m = (j < ncoords) ? coords[j++] : 0; write_posn_geojson_4(wr, point, z, m); break; default: // 2 write_posn_geojson(wr, point); break; } } } write_char(wr, ']'); write_char(wr, '}'); } static void write_geom_multilinestring_geojson(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "{\"type\":\"MultiLineString\",\"coordinates\":["); if (geom->multi) { double *pcoords = (double *)geom->coords; int ncoords = geom->ncoords; int n; for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_line *line = tg_geom_line(geom->multi->geoms[i]); const struct tg_ring *ring = (struct tg_ring*)line; if (i > 0) write_char(wr, ','); switch (tg_geom_dims(geom)) { case 3: n = write_ring_points_geojson_3(wr, ring, pcoords, ncoords); ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; break; case 4: n = write_ring_points_geojson_4(wr, ring, pcoords, ncoords); ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); break; default: // 2 write_ring_points_geojson(wr, ring); break; } } } write_char(wr, ']'); write_char(wr, '}'); } static void write_geom_multipolygon_geojson(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "{\"type\":\"MultiPolygon\",\"coordinates\":["); if (geom->multi) { double *pcoords = (double *)geom->coords; int ncoords = geom->ncoords; int n; for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_poly *poly = tg_geom_poly(geom->multi->geoms[i]); if (i > 0) write_char(wr, ','); switch (tg_geom_dims(geom)) { case 3: n = write_poly_points_geojson_3(wr, poly, pcoords, ncoords); ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; break; case 4: n = write_poly_points_geojson_4(wr, poly, pcoords, ncoords); ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); break; default: // 2 write_poly_points_geojson(wr, poly); break; } } } write_char(wr, ']'); write_char(wr, '}'); } static void write_geom_geojson(const struct tg_geom *geom, struct writer *wr); static void write_geom_geometrycollection_geojson(const struct tg_geom *geom, struct writer *wr) { if ((getflags(geom)&IS_FEATURE_COL) == IS_FEATURE_COL) { write_string(wr, "{\"type\":\"FeatureCollection\",\"features\":["); } else { write_string(wr, "{\"type\":\"GeometryCollection\",\"geometries\":["); } int ngeoms = tg_geom_num_geometries(geom); for (int i = 0; i < ngeoms; i++) { if (i > 0) write_char(wr, ','); write_geom_geojson(tg_geom_geometry_at(geom, i), wr); } write_char(wr, ']'); write_char(wr, '}'); } static void write_base_geom_geojson(const struct tg_geom *geom, struct writer *wr) { if ((getflags(geom)&IS_ERROR) == IS_ERROR) { // sigh, just write us an empty point ... write_string(wr, "{\"type\":\"Point\",\"coordinates\":[]}"); return; } bool is_feat = (getflags(geom)&IS_FEATURE) == IS_FEATURE; struct json fjson = { 0 }; struct json gjson = { 0 }; const char *xjson = getbase(geom) == BASE_GEOM ? geom->xjson : NULL; if (is_feat) { if (xjson) { struct json json = json_parse(xjson); if (json_type(json) == JSON_ARRAY) { fjson = json_ensure(json_first(json)); gjson = json_ensure(json_next(fjson)); } else if (json_type(json) == JSON_OBJECT) { fjson = json_ensure(json); } } write_string(wr, "{\"type\":\"Feature\","); struct json id = json_object_get(fjson, "id"); if (json_exists(id)) { write_string(wr, "\"id\":"); write_stringn(wr, json_raw(id), json_raw_length(id)); write_char(wr, ','); } write_string(wr, "\"geometry\":"); } else { if (xjson) { gjson = json_ensure(json_parse(xjson)); } } if (is_feat && (getflags(geom)&IS_UNLOCATED) == IS_UNLOCATED) { write_string(wr, "null"); } else { switch (gettype(geom)) { case TG_POINT: write_geom_point_geojson(geom, wr); break; case TG_LINESTRING: write_geom_linestring_geojson(geom, wr); break; case TG_POLYGON: write_geom_polygon_geojson(geom, wr); break; case TG_MULTIPOINT: write_geom_multipoint_geojson(geom, wr); break; case TG_MULTILINESTRING: write_geom_multilinestring_geojson(geom, wr); break; case TG_MULTIPOLYGON: write_geom_multipolygon_geojson(geom, wr); break; case TG_GEOMETRYCOLLECTION: write_geom_geometrycollection_geojson(geom, wr); break; } } if (json_type(gjson) == JSON_OBJECT) { if (json_exists(json_first(gjson))) { long len = json_raw_length(gjson)-1; if (len > 1) { // rewind one byte wr->count--; write_char(wr, ','); write_stringn(wr, (char*)(json_raw(gjson)+1), len); } } } if (is_feat) { bool wrote_props = false; if (json_type(fjson) == JSON_OBJECT) { struct json key = json_first(fjson); struct json val = json_next(key); while (json_exists(key)) { if (json_raw_compare(key, "\"id\"") != 0) { write_char(wr, ','); write_stringn(wr, json_raw(key), json_raw_length(key)); write_char(wr, ':'); write_stringn(wr, json_raw(val), json_raw_length(val)); if (!wrote_props && json_raw_compare(key, "\"properties\"") == 0) { wrote_props = true; } } key = json_next(val); val = json_next(key); } } if (!wrote_props) { write_string(wr, ",\"properties\":"); if ((getflags(geom)&HAS_NULL_PROPS)== HAS_NULL_PROPS){ write_string(wr, "null"); } else { write_string(wr, "{}"); } } write_char(wr, '}'); } } static void write_point_geojson(const struct boxed_point *point, struct writer *wr) { if ((getflags(point)&IS_FEATURE) == IS_FEATURE) { write_string(wr, "{\"type\":\"Feature\",\"geometry\":"); } write_string(wr, "{\"type\":\"Point\",\"coordinates\":"); write_posn_geojson(wr, point->point); write_char(wr, '}'); if ((getflags(point)&IS_FEATURE) == IS_FEATURE) { write_string(wr, ",\"properties\":"); if ((getflags(point)&HAS_NULL_PROPS)== HAS_NULL_PROPS){ write_string(wr, "null}"); } else { write_string(wr, "{}}"); } } } static void write_line_geojson(const struct tg_line *line, struct writer *wr) { struct tg_ring *ring = (struct tg_ring*)line; if ((getflags(ring)&IS_FEATURE) == IS_FEATURE) { write_string(wr, "{\"type\":\"Feature\",\"geometry\":"); } write_string(wr, "{\"type\":\"LineString\",\"coordinates\":"); write_ring_points_geojson(wr, ring); write_char(wr, '}'); if ((getflags(ring)&IS_FEATURE) == IS_FEATURE) { write_string(wr, ",\"properties\":"); if ((getflags(ring)&HAS_NULL_PROPS)== HAS_NULL_PROPS){ write_string(wr, "null}"); } else { write_string(wr, "{}}"); } } } static void write_ring_geojson(const struct tg_ring *ring, struct writer *wr) { if ((getflags(ring)&IS_FEATURE) == IS_FEATURE) { write_string(wr, "{\"type\":\"Feature\",\"geometry\":"); } write_string(wr, "{\"type\":\"Polygon\",\"coordinates\":["); write_ring_points_geojson(wr, ring); write_string(wr, "]}"); if ((getflags(ring)&IS_FEATURE) == IS_FEATURE) { write_string(wr, ",\"properties\":"); if ((getflags(ring)&HAS_NULL_PROPS)== HAS_NULL_PROPS){ write_string(wr, "null}"); } else { write_string(wr, "{}}"); } } } static void write_poly_geojson(const struct tg_poly *poly, struct writer *wr) { if ((getflags(poly)&IS_FEATURE) == IS_FEATURE) { write_string(wr, "{\"type\":\"Feature\",\"geometry\":"); } write_string(wr, "{\"type\":\"Polygon\",\"coordinates\":"); write_poly_points_geojson(wr, poly); write_char(wr, '}'); if ((getflags(poly)&IS_FEATURE) == IS_FEATURE) { write_string(wr, ",\"properties\":"); if ((getflags(poly)&HAS_NULL_PROPS)== HAS_NULL_PROPS){ write_string(wr, "null}"); } else { write_string(wr, "{}}"); } } } static void write_geom_geojson(const struct tg_geom *geom, struct writer *wr) { switch (getbase(geom)) { case BASE_GEOM: write_base_geom_geojson(geom, wr); break; case BASE_POINT: write_point_geojson((struct boxed_point*)geom, wr); break; case BASE_LINE: write_line_geojson((struct tg_line*)geom, wr); break; case BASE_RING: write_ring_geojson((struct tg_ring*)geom, wr); break; case BASE_POLY: write_poly_geojson((struct tg_poly*)geom, wr); break; } } /// Writes a GeoJSON representation of a geometry. /// /// The content is stored as a C string in the buffer pointed to by dst. /// A terminating null character is automatically appended after the /// content written. /// /// @param geom Input geometry /// @param dst Buffer where the resulting content is stored. /// @param n Maximum number of bytes to be used in the buffer. /// @return The number of characters, not including the null-terminator, /// needed to store the content into the C string buffer. /// If the returned length is greater than n-1, then only a partial copy /// occurred, for example: /// /// ``` /// char str[64]; /// size_t len = tg_geom_geojson(geom, str, sizeof(str)); /// if (len > sizeof(str)-1) { /// // ... write did not complete ... /// } /// ``` /// /// @see tg_geom_wkt() /// @see tg_geom_wkb() /// @see tg_geom_hex() /// @see GeometryWriting size_t tg_geom_geojson(const struct tg_geom *geom, char *dst, size_t n) { if (!geom) return 0; struct writer wr = { .dst = (uint8_t*)dst, .n = n }; write_geom_geojson(geom, &wr); write_nullterm(&wr); return wr.count; } /// Returns a string that represents any extra JSON from a parsed GeoJSON /// geometry. Such as the "id" or "properties" fields. /// @param geom Input geometry /// @return Returns a valid JSON object as a string, or NULL if the geometry /// did not come from GeoJSON or there is no extra JSON. /// @note The returned string does not need to be freed. /// @see tg_parse_geojson() const char *tg_geom_extra_json(const struct tg_geom *geom) { return geom && getbase(geom) == BASE_GEOM && (getflags(geom)&IS_ERROR) != IS_ERROR ? geom->xjson : NULL; } ////////////////// // wkt ////////////////// static const char *wkt_invalid_err(const char *inner) { (void)inner; return "invalid text"; } static bool isws(char c) { return c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\r'); } static bool isnum(char c) { return c == '-' || (c >= '0' && c <= '9'); } static long wkt_trim_ws(const char *wkt, long len, long i) { for (; i < len; i++) { if (!isws(wkt[i])) break; } return i; } // returns -1 for invalid 'Z', 'M', 'ZM' or 'EMPTY' static enum tg_geom_type wkt2type(const char *wkt, long len, bool *z, bool *m, bool *empty) { *z = false; *m = false; *empty = false; char name[32]; long i = 0; long j = 0; int nws = 0; for (; i < len; i++) { if (j == sizeof(name)-1) { goto bad_type; } if (isws(wkt[i])) { if (j > 0 && name[j-1] == ' ') continue; name[j] = ' '; nws++; if (nws > 2) return -1; } else if (wkt[i] >= 'a' && wkt[i] <= 'z') { name[j] = wkt[i]-32; } else { name[j] = wkt[i]; } j++; } name[j] = '\0'; if (j > 6) { // look for " EMPTY" suffix if (strcmp(name+j-6, " EMPTY") == 0) { j -= 6; name[j] = '\0'; *empty = true; } } if (j > 3) { // look for " ZM", " Z", " M" and smash it if (name[j-2] == 'Z' && name[j-1] == 'M') { j -= 2; *z = true; *m = true; } else if (name[j-1] == 'Z') { j -= 1; *z = true; } else if (name[j-1] == 'M') { j -= 1; *m = true; } if (name[j-1] == ' ') { j -= 1; } name[j] = '\0'; } if (j == 0) return 0; if (!strcmp(name, "POINT")) return TG_POINT; if (!strcmp(name, "LINESTRING")) return TG_LINESTRING; if (!strcmp(name, "POLYGON")) return TG_POLYGON; if (!strcmp(name, "MULTIPOINT")) return TG_MULTIPOINT; if (!strcmp(name, "MULTILINESTRING")) return TG_MULTILINESTRING; if (!strcmp(name, "MULTIPOLYGON")) return TG_MULTIPOLYGON; if (!strcmp(name, "GEOMETRYCOLLECTION")) return TG_GEOMETRYCOLLECTION; if (strchr(name, ' ')) return -1; bad_type: // determine the length of the bad type i = 0; for (; i < len; i++) { char c = wkt[i]; if (isws(c)) break; if (c >= 'a' && c <= 'z') c -= 32; if (c < 'A' || c > 'Z') break; } return -(i+1); } static long wkt_balance_coords(const char *wkt, long len, long i) { i++; // first '(' already checked by caller long depth = 1; long maxdepth = 1; for (; i < len; i++) { if (wkt[i] == '(') { depth++; maxdepth++; } else if (wkt[i] == ')') { depth--; if (depth == 0) { if (maxdepth > MAXDEPTH) { return -(i+1); } return i+1; } } } return -(i+1); } static long wkt_vnumber(const char *data, long dlen, long i) { // sign if (data[i] == '-') { i++; if (i == dlen) return -(i+1); } // int if ((data[i] < '0' || data[i] > '9') && data[i] != '.') return -(i+1); for (; i < dlen; i++) { if (data[i] >= '0' && data[i] <= '9') continue; break; } // frac if (i == dlen) return i; if (data[i] == '.') { i++; if (i == dlen) return -(i+1); if (data[i] < '0' || data[i] > '9') return -(i+1); i++; for (; i < dlen; i++) { if (data[i] >= '0' && data[i] <= '9') continue; break; } } // exp if (i == dlen) return i; if (data[i] == 'e' || data[i] == 'E') { i++; if (i == dlen) return -(i+1); if (data[i] == '+' || data[i] == '-') i++; if (i == dlen) return -(i+1); if (data[i] < '0' || data[i] > '9') return -(i+1); i++; for (; i < dlen; i++) { if (data[i] >= '0' && data[i] <= '9') continue; break; } } return i; } static const char *err_for_wkt_posn(int dims) { if (dims == 2) { return "each position must have two numbers"; } else if (dims == 3) { return "each position must have three numbers"; } else if (dims == 4) { return "each position must have four numbers"; } else { return "each position must have two to four numbers"; } } static struct tg_geom *parse_wkt_point(const char *wkt, long len, bool z, bool m, enum tg_index ix) { (void)ix; int dims = z ? m ? 4 : 3 : m ? 3 : 0; long i = 0; double posn[4] = { 0 }; int pdims = 0; i = wkt_trim_ws(wkt, len, i); if (i == len) goto bad_dims; while (1) { long s = i; if ((i = wkt_vnumber(wkt, len, i)) < 0) { return make_parse_error(wkt_invalid_err("invalid number")); } if (pdims < 4) { posn[pdims++] = strtod(wkt+s, NULL); } else goto bad_dims; if (i == len) break; if (isws(wkt[i])) { i = wkt_trim_ws(wkt, len, i); if (i == len) break; } else { return make_parse_error(wkt_invalid_err("invalid number")); } } enum flags flags = 0; if (dims == 0) { if (pdims < 2) goto bad_dims; } else { if (pdims != dims) goto bad_dims; } dims = pdims; struct tg_point pt = { posn[0], posn[1] }; struct tg_geom *geom = NULL; if (dims == 2) { geom = tg_geom_new_point(pt); } else if (dims == 3) { if (m) { geom = tg_geom_new_point_m(pt, posn[2]); } else { geom = tg_geom_new_point_z(pt, posn[2]); } } else { geom = tg_geom_new_point_zm(pt, posn[2], posn[3]); } if (geom) setflags(geom, flags); return geom; bad_dims: return make_parse_error("%s", err_for_wkt_posn(dims)); } static int parse_wkt_posns(enum base base, int dims, int depth, const char *wkt, long len, struct dvec *posns, struct dvec *xcoords, const char **err) { (void)depth; // TODO: return correct depth errors double posn[4] = { 0 }; int pdims = 0; long i = 0; i = wkt_trim_ws(wkt, len, i); if (i == len) { // err: expected numbers goto exp_nums; } bool xparens = false; if (base == BASE_POINT && wkt[i] == '(') { // The multipoint is using the format 'MULTIPOINT ((1 2),(3 4))'. // While not standard, it must be supported. xparens = true; } while (i < len) { if (xparens) { if (i == len || wkt[i] != '(') { // err: expected '(' *err = wkt_invalid_err("expected '('"); return -1; } i = wkt_trim_ws(wkt, len, i+1); } // read each number, delimited by whitespace while (i < len) { double num; if (isnum(wkt[i])) { long s = i; i = wkt_vnumber(wkt, len, i); if (i < 0) { *err = wkt_invalid_err("invalid number"); return -1; } num = strtod(wkt+s, NULL); } else if (wkt[i] == ')') { // err: expected a number, got ')' *err = wkt_invalid_err("expected number, got '('"); return -1; } else if (wkt[i] == ',') { *err = wkt_invalid_err("expected number, got ','"); return -1; } else { *err = wkt_invalid_err("expected a number"); return -1; } if (pdims == 4) { *err = err_for_wkt_posn(dims); return -1; } posn[pdims++] = num; if (i == len || !isws(wkt[i])) break; if ((i = wkt_trim_ws(wkt, len, i+1)) == len) break; if (wkt[i] == ')' || wkt[i] == ',') break; } if (xparens) { if (i == len || wkt[i] != ')') { *err = wkt_invalid_err("expected ')'"); return -1; } i = wkt_trim_ws(wkt, len, i+1); } if (i < len) { if (wkt[i] != ',') { *err = wkt_invalid_err("expected ','"); return -1; } i = wkt_trim_ws(wkt, len, i+1); if (i == len) { *err = wkt_invalid_err("expected position, got end of stream"); return -1; } } if (dims != pdims) { if (dims == 0 && pdims >= 2) { dims = pdims; } else { *err = err_for_wkt_posn(dims); return -1; } } if (!dvec_append(posns, posn[0]) || !dvec_append(posns, posn[1])) { return -1; } for (int i = 2; i < dims; i++) { if (i >= pdims || !dvec_append(xcoords, posn[i])) return -1; } pdims = 0; } exp_nums: if (!check_parse_posns(base, posns->data, posns->len, err)) return -1; return dims; } static int parse_wkt_multi_posns(enum base base, int dims, int depth, const char *wkt, long len, struct dvec *posns, struct rvec *rings, struct tg_poly **poly, struct dvec *xcoords, enum tg_index ix, const char **err) { long i = 0; i = wkt_trim_ws(wkt, len, i); while (i < len) { if (wkt[i] != '(') { *err = wkt_invalid_err("expected '('"); return -1; } long j = wkt_balance_coords(wkt, len, i); const char *grp_wkt = wkt+i+1; long grp_len = j-i-2; i = j; posns->len = 0; dims = parse_wkt_posns(base, dims, depth, grp_wkt, grp_len, posns, xcoords, err); if (dims == -1) return -1; struct tg_ring *ring = tg_ring_new_ix((struct tg_point*)posns->data, posns->len / 2, ix); if (!ring) return -1; if (!rvec_append(rings, ring)) { tg_ring_free(ring); return -1; } i = wkt_trim_ws(wkt, len, i); if (i == len) break; if (wkt[i] != ',') { *err = wkt_invalid_err("expected ','"); return -1; } i = wkt_trim_ws(wkt, len, i+1); if (i == len) { *err = wkt_invalid_err("expected '(', got end of stream"); return -1; } } if (rings->len == 0) { *err = "polygons must have one or more rings"; return -1; } *poly = tg_poly_new(rings->data[0], (struct tg_ring const*const*)rings->data+1, rings->len-1); if (!*poly) return -1; for (size_t i = 0; i < rings->len; i++) { tg_ring_free(rings->data[i]); } rings->len = 0; return dims; } static struct tg_geom *parse_wkt_linestring(const char *wkt, long len, bool z, bool m, enum tg_index ix) { struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct tg_line *line = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; const char *err = NULL; int dims = z ? m ? 4 : 3 : m ? 3 : 0; dims = parse_wkt_posns(BASE_LINE, dims, 1, wkt, len, &posns, &xcoords, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } line = tg_line_new_ix((struct tg_point*)posns.data, posns.len / 2, ix); if (!line) goto fail; switch (dims) { case 2: geom = tg_geom_new_linestring(line); break; case 3: if (m) { geom = tg_geom_new_linestring_m(line, xcoords.data, xcoords.len); } else { geom = tg_geom_new_linestring_z(line, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_linestring_zm(line, xcoords.data, xcoords.len); break; } cleanup: tg_line_free(line); if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); return geom; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; } static struct tg_geom *parse_wkt_polygon(const char *wkt, long len, bool z, bool m, enum tg_index ix) { struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct tg_poly *poly = NULL; struct rvec rings = { 0 }; const char *err = NULL; int dims = z ? m ? 4 : 3 : m ? 3 : 0; dims = parse_wkt_multi_posns(BASE_RING, dims, 2, wkt, len, &posns, &rings, &poly, &xcoords, ix, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } switch (dims) { case 2: geom = tg_geom_new_polygon(poly); break; case 3: if (m) { geom = tg_geom_new_polygon_m(poly, xcoords.data, xcoords.len); } else { geom = tg_geom_new_polygon_z(poly, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_polygon_zm(poly, xcoords.data, xcoords.len); break; } cleanup: tg_poly_free(poly); if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); if (rings.data) { for (size_t i = 0; i < rings.len; i++) { tg_ring_free(rings.data[i]); } tg_free(rings.data); } return geom; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; } static struct tg_geom *parse_wkt_multipoint(const char *wkt, long len, bool z, bool m, enum tg_index ix) { (void)ix; struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; const char *err = NULL; int dims = z ? m ? 4 : 3 : m ? 3 : 0; dims = parse_wkt_posns(BASE_POINT, dims, 1, wkt, len, &posns, &xcoords, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } const struct tg_point *points = (struct tg_point*)posns.data; int npoints = posns.len/2; switch (dims) { case 2: geom = tg_geom_new_multipoint(points, npoints); break; case 3: if (m) { geom = tg_geom_new_multipoint_m(points, npoints, xcoords.data, xcoords.len); } else { geom = tg_geom_new_multipoint_z(points, npoints, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_multipoint_zm(points, npoints, xcoords.data, xcoords.len); break; } cleanup: if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); return geom; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; } static struct tg_geom *parse_wkt_multilinestring(const char *wkt, long len, bool z, bool m, enum tg_index ix) { int dims = z ? m ? 4 : 3 : m ? 3 : 0; struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct lvec lines = { 0 }; const char *err = NULL; long i = wkt_trim_ws(wkt, len, 0); while (i < len) { if (wkt[i] != '(') { gerr = make_parse_error(wkt_invalid_err("expected '('")); goto fail; } long j = wkt_balance_coords(wkt, len, i); const char *grp_wkt = wkt+i+1; long grp_len = j-i-2; i = j; posns.len = 0; dims = parse_wkt_posns(BASE_LINE, dims, 2, grp_wkt, grp_len, &posns, &xcoords, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } struct tg_line *line = tg_line_new_ix((struct tg_point*)posns.data, posns.len / 2, ix); if (!line) goto fail; if (!lvec_append(&lines, line)) { tg_line_free(line); goto fail; } i = wkt_trim_ws(wkt, len, i); if (i == len) break; if (wkt[i] != ',') { gerr = make_parse_error(wkt_invalid_err("expected ','")); goto fail; } i = wkt_trim_ws(wkt, len, i+1); if (i == len) { gerr = make_parse_error(wkt_invalid_err("expected '('")); goto fail; } } switch (dims) { case 2: geom = tg_geom_new_multilinestring( (struct tg_line const*const*)lines.data, lines.len); break; case 3: if (m) { geom = tg_geom_new_multilinestring_m( (struct tg_line const*const*)lines.data, lines.len, xcoords.data, xcoords.len); } else { geom = tg_geom_new_multilinestring_z( (struct tg_line const*const*)lines.data, lines.len, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_multilinestring_zm( (struct tg_line const*const*)lines.data, lines.len, xcoords.data, xcoords.len); break; } cleanup: if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); if (lines.data) { for (size_t i = 0; i < lines.len; i++) { tg_line_free(lines.data[i]); } tg_free(lines.data); } return geom; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; } static struct tg_geom *parse_wkt_multipolygon(const char *wkt, long len, bool z, bool m, enum tg_index ix) { int dims = z ? m ? 4 : 3 : m ? 3 : 0; struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct tg_poly *poly = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct rvec rings = { 0 }; struct pvec polys = { 0 }; const char *err = NULL; long i = wkt_trim_ws(wkt, len, 0); while (i < len) { if (wkt[i] != '(') { gerr = make_parse_error(wkt_invalid_err("expected '('")); goto fail; } long j = wkt_balance_coords(wkt, len, i); const char *grp_wkt = wkt+i+1; long grp_len = j-i-2; i = j; posns.len = 0; rings.len = 0; dims = parse_wkt_multi_posns(BASE_RING, dims, 3, grp_wkt, grp_len, &posns, &rings, &poly, &xcoords, ix, &err); if (dims == -1) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } if (!pvec_append(&polys, poly)) { tg_poly_free(poly); goto fail; } i = wkt_trim_ws(wkt, len, i); if (i == len) break; if (wkt[i] != ',') { gerr = make_parse_error(wkt_invalid_err("expected ','")); goto fail; } i = wkt_trim_ws(wkt, len, i+1); if (i == len) { gerr = make_parse_error(wkt_invalid_err("expected '('")); goto fail; } } switch (dims) { case 2: geom = tg_geom_new_multipolygon( (struct tg_poly const*const*)polys.data, polys.len); break; case 3: if (m) { geom = tg_geom_new_multipolygon_m( (struct tg_poly const*const*)polys.data, polys.len, xcoords.data, xcoords.len); } else { geom = tg_geom_new_multipolygon_z( (struct tg_poly const*const*)polys.data, polys.len, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_multipolygon_zm( (struct tg_poly const*const*)polys.data, polys.len, xcoords.data, xcoords.len); break; } cleanup: if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); if (polys.data) { for (size_t i = 0; i < polys.len; i++) { tg_poly_free(polys.data[i]); } tg_free(polys.data); } if (rings.data) { for (size_t i = 0; i < rings.len; i++) { tg_ring_free(rings.data[i]); } tg_free(rings.data); } return geom; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; } static long wkt_next_geometry(const char *wkt, long len, long i) { for (;i < len;i++) { if (wkt[i] == ',') break; if (wkt[i] == '(') { return wkt_balance_coords(wkt, len, i); } } return i; } static struct tg_geom *parse_wkt(const char *wkt, long len, enum tg_index ix); static struct tg_geom *parse_wkt_geometrycollection(const char *wkt, long len, bool z, bool m, enum tg_index ix) { (void)z; (void)m; // not used struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct gvec geoms = { 0 }; long i = 0; size_t commas = 0; while (i < len) { long s = i; i = wkt_next_geometry(wkt, len, i); if (i-s > 0) { struct tg_geom *child = parse_wkt(wkt+s, i-s, ix); if (!child) goto fail; if (tg_geom_error(child)) { gerr = child; child = NULL; goto fail; } if (!gvec_append(&geoms, child)) { tg_geom_free(child); goto fail; } } i = wkt_trim_ws(wkt, len, i); if (i == len) break; if (wkt[i] != ',') { gerr = make_parse_error(wkt_invalid_err("expected ','")); goto fail; } i = wkt_trim_ws(wkt, len, i); commas++; i++; } if (commas+1 != geoms.len) { // err: missing last geometry gerr = make_parse_error("missing type"); goto fail; } geom = tg_geom_new_geometrycollection( (struct tg_geom const*const*)geoms.data, geoms.len); cleanup: if (geoms.data) { for (size_t i = 0; i < geoms.len; i++) { tg_geom_free(geoms.data[i]); } tg_free(geoms.data); } return geom; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; } static struct tg_geom *parse_wkt(const char *wkt, long len, enum tg_index ix) { if (len == 0) { return make_parse_error("missing type"); } long i = wkt_trim_ws(wkt, len, 0); long s = i; for (; i < len; i++) if (wkt[i] == '(') break; long e = i; while (e-1 > s && isws(wkt[e-1])) e--; bool z, m, is_empty; enum tg_geom_type type = wkt2type(wkt+s, e-s, &z, &m, &is_empty); if ((int)type <= 0) { int n = (int)type; if (n == 0) { return make_parse_error("missing type"); } else if (n == -1) { return make_parse_error("invalid type specifier, " "expected 'Z', 'M', 'ZM', or 'EMPTY'"); } else { n = (-n)-1; return make_parse_error("unknown type '%.*s'", n, wkt+s); } } if (is_empty) { switch (type) { case TG_POINT: return tg_geom_new_point_empty(); case TG_LINESTRING: return tg_geom_new_linestring_empty(); case TG_POLYGON: return tg_geom_new_polygon_empty(); case TG_MULTIPOINT: return tg_geom_new_multipoint_empty(); case TG_MULTILINESTRING: return tg_geom_new_multilinestring_empty(); case TG_MULTIPOLYGON: return tg_geom_new_multipolygon_empty(); default: return tg_geom_new_geometrycollection_empty(); } } if (i == len || wkt[i] != '(') { return make_parse_error(wkt_invalid_err("expected '('")); } long j = wkt_balance_coords(wkt, len, i); if (j <= 0) { return make_parse_error(wkt_invalid_err("unbalanced '()'")); } long k = j; for (; k < len; k++) { if (!isws(wkt[k])) { return make_parse_error( wkt_invalid_err("too much data after last ')'")); } } // Only use the inner parts of the group. Do not include the parens. wkt = wkt+i+1; len = j-i-2; switch (type) { case TG_POINT: return parse_wkt_point(wkt, len, z, m, ix); case TG_LINESTRING: return parse_wkt_linestring(wkt, len, z, m, ix); case TG_POLYGON: return parse_wkt_polygon(wkt, len, z, m, ix); case TG_MULTIPOINT: return parse_wkt_multipoint(wkt, len, z, m, ix); case TG_MULTILINESTRING: return parse_wkt_multilinestring(wkt, len, z, m, ix); case TG_MULTIPOLYGON: return parse_wkt_multipolygon(wkt, len, z, m, ix); default: return parse_wkt_geometrycollection(wkt, len, z, m, ix); } } /// Parse Well-known text (WKT) with an included data length. /// @param wkt WKT data /// @param len Length of data /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_wkt() /// @see GeometryParsing struct tg_geom *tg_parse_wktn(const char *wkt, size_t len) { return tg_parse_wktn_ix(wkt, len, TG_DEFAULT); } /// Parse Well-known text (WKT). /// @param wkt A WKT string. Must be null-terminated /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_wktn() /// @see tg_parse_wkt_ix() /// @see tg_parse_wktn_ix() /// @see tg_geom_error() /// @see tg_geom_wkt() /// @see GeometryParsing struct tg_geom *tg_parse_wkt(const char *wkt) { return tg_parse_wktn_ix(wkt, wkt?strlen(wkt):0, TG_DEFAULT); } /// Parse Well-known text (WKT) using provided indexing option. /// @param wkt A WKT string. Must be null-terminated /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_wkt() /// @see tg_parse_wktn_ix() /// @see GeometryParsing struct tg_geom *tg_parse_wkt_ix(const char *wkt, enum tg_index ix) { return tg_parse_wktn_ix(wkt, wkt?strlen(wkt):0, ix); } /// Parse Well-known text (WKT) using provided indexing option. /// @param wkt WKT data /// @param len Length of data /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_wkt() /// @see tg_parse_wkt_ix() /// @see GeometryParsing struct tg_geom *tg_parse_wktn_ix(const char *wkt, size_t len, enum tg_index ix) { struct tg_geom *geom = parse_wkt(wkt, len, ix); if (!geom) return NULL; if ((getflags(geom)&IS_ERROR) == IS_ERROR) { struct tg_geom *gerr = make_parse_error("ParseError: %s", geom->error); tg_geom_free(geom); return gerr; } return geom; } static void write_posn_wkt(struct writer *wr, struct tg_point posn) { write_string_double(wr, posn.x); write_char(wr, ' '); write_string_double(wr, posn.y); } static void write_posn_wkt_3(struct writer *wr, struct tg_point posn, double z) { write_string_double(wr, posn.x); write_char(wr, ' '); write_string_double(wr, posn.y); write_char(wr, ' '); write_string_double(wr, z); } static void write_posn_wkt_4(struct writer *wr, struct tg_point posn, double z, double m) { write_string_double(wr, posn.x); write_char(wr, ' '); write_string_double(wr, posn.y); write_char(wr, ' '); write_string_double(wr, z); write_char(wr, ' '); write_string_double(wr, m); } static void write_point_wkt(const struct boxed_point *point, struct writer *wr) { write_string(wr, "POINT("); write_posn_wkt(wr, point->point); write_char(wr, ')'); } static int write_ring_points_wkt(struct writer *wr, const struct tg_ring *ring) { for (int i = 0 ; i < ring->npoints; i++) { if (i > 0) write_char(wr, ','); write_posn_wkt(wr, ring->points[i]); } return ring->npoints; } static int write_ring_points_wkt_3(struct writer *wr, const struct tg_ring *ring, const double *coords, int ncoords) { double z; int j = 0; for (int i = 0 ; i < ring->npoints; i++) { if (i > 0) write_char(wr, ','); z = (j < ncoords) ? coords[j++] : 0; write_posn_wkt_3(wr, ring->points[i], z); } return ring->npoints; } static int write_ring_points_wkt_4(struct writer *wr, const struct tg_ring *ring, const double *coords, int ncoords) { double z, m; int j = 0; for (int i = 0 ; i < ring->npoints; i++) { if (i > 0) write_char(wr, ','); z = (j < ncoords) ? coords[j++] : 0; m = (j < ncoords) ? coords[j++] : 0; write_posn_wkt_4(wr, ring->points[i], z, m); } return ring->npoints; } static void write_line_wkt(const struct tg_line *line, struct writer *wr) { struct tg_ring *ring = (struct tg_ring*)line; write_string(wr, "LINESTRING("); write_ring_points_wkt(wr, ring); write_char(wr, ')'); } static void write_ring_wkt(const struct tg_ring *ring, struct writer *wr) { write_string(wr, "POLYGON(("); write_ring_points_wkt(wr, ring); write_string(wr, "))"); } static int write_poly_points_wkt(struct writer *wr, const struct tg_poly *poly) { int count = 0; write_char(wr, '('); write_ring_points_wkt(wr, tg_poly_exterior(poly)); write_char(wr, ')'); int nholes = tg_poly_num_holes(poly); for (int i = 0 ; i < nholes; i++) { write_char(wr, ','); write_char(wr, '('); count += write_ring_points_wkt(wr, tg_poly_hole_at(poly, i)); write_char(wr, ')'); } return count; } static int write_poly_points_wkt_3(struct writer *wr, const struct tg_poly *poly, const double *coords, int ncoords) { int count = 0; double *pcoords = (double*)coords; const struct tg_ring *exterior = tg_poly_exterior(poly); write_char(wr, '('); int n = write_ring_points_wkt_3(wr, exterior, pcoords, ncoords); write_char(wr, ')'); count += n; ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; int nholes = tg_poly_num_holes(poly); for (int i = 0 ; i < nholes; i++) { write_char(wr, ','); const struct tg_ring *hole = tg_poly_hole_at(poly, i); write_char(wr, '('); int n = write_ring_points_wkt_3(wr, hole, pcoords, ncoords); write_char(wr, ')'); count += n; ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; } return count; } static int write_poly_points_wkt_4(struct writer *wr, const struct tg_poly *poly, const double *coords, int ncoords) { int count = 0; double *pcoords = (double*)coords; const struct tg_ring *exterior = tg_poly_exterior(poly); write_char(wr, '('); int n = write_ring_points_wkt_4(wr, exterior, pcoords, ncoords); write_char(wr, ')'); count += n; ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); int nholes = tg_poly_num_holes(poly); for (int i = 0 ; i < nholes; i++) { write_char(wr, ','); const struct tg_ring *hole = tg_poly_hole_at(poly, i); write_char(wr, '('); int n = write_ring_points_wkt_4(wr, hole, pcoords, ncoords); write_char(wr, ')'); count += n; ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); } return count; } static void write_poly_wkt(const struct tg_poly *poly, struct writer *wr) { write_string(wr, "POLYGON("); write_poly_points_wkt(wr, poly); write_char(wr, ')'); } static void write_zm_def_wkt(struct writer *wr, const struct tg_geom *geom) { if ((getflags(geom)&HAS_M) == HAS_M && (getflags(geom)&HAS_Z) != HAS_Z) { write_string(wr, " M"); } } static void write_geom_point_wkt(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "POINT"); write_zm_def_wkt(wr, geom); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_string(wr, " EMPTY"); } else { write_char(wr, '('); if ((getflags(geom)&HAS_Z) == HAS_Z) { if ((getflags(geom)&HAS_M) == HAS_M) { write_posn_wkt_4(wr, geom->point, geom->z, geom->m); } else { write_posn_wkt_3(wr, geom->point, geom->z); } } else if ((getflags(geom)&HAS_M) == HAS_M) { write_posn_wkt_3(wr, geom->point, geom->m); } else { write_posn_wkt(wr, geom->point); } write_char(wr, ')'); } } static void write_geom_linestring_wkt(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "LINESTRING"); write_zm_def_wkt(wr, geom); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_string(wr, " EMPTY"); return; } write_char(wr, '('); switch (tg_geom_dims(geom)) { case 3: write_ring_points_wkt_3(wr, (struct tg_ring*)geom->line, geom->coords, geom->ncoords); break; case 4: write_ring_points_wkt_4(wr, (struct tg_ring*)geom->line, geom->coords, geom->ncoords); break; default: write_ring_points_wkt(wr, (struct tg_ring*)geom->line); break; } write_char(wr, ')'); } static void write_geom_polygon_wkt(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "POLYGON"); write_zm_def_wkt(wr, geom); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_string(wr, " EMPTY"); return; } write_char(wr, '('); switch (tg_geom_dims(geom)) { case 3: write_poly_points_wkt_3(wr, geom->poly, geom->coords, geom->ncoords); break; case 4: write_poly_points_wkt_4(wr, geom->poly, geom->coords, geom->ncoords); break; default: // 2 write_poly_points_wkt(wr, geom->poly); break; } write_char(wr, ')'); } static void write_geom_multipoint_wkt(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "MULTIPOINT"); write_zm_def_wkt(wr, geom); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY || !geom->multi || !geom->multi->ngeoms) { write_string(wr, " EMPTY"); return; } write_char(wr, '('); int dims = tg_geom_dims(geom); double z, m; double *coords = (double *)geom->coords; int ncoords = geom->ncoords; int j = 0; for (int i = 0; i < geom->multi->ngeoms; i++) { struct tg_point point = tg_geom_point(geom->multi->geoms[i]); if (i > 0) write_char(wr, ','); switch (dims) { case 3: z = (j < ncoords) ? coords[j++] : 0; write_posn_wkt_3(wr, point, z); break; case 4: z = (j < ncoords) ? coords[j++] : 0; m = (j < ncoords) ? coords[j++] : 0; write_posn_wkt_4(wr, point, z, m); break; default: // 2 write_posn_wkt(wr, point); break; } } write_char(wr, ')'); } static void write_geom_multilinestring_wkt(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "MULTILINESTRING"); write_zm_def_wkt(wr, geom); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY || !geom->multi || !geom->multi->ngeoms) { write_string(wr, " EMPTY"); return; } write_char(wr, '('); double *pcoords = (double *)geom->coords; int ncoords = geom->ncoords; int n; for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_line *line = tg_geom_line( geom->multi->geoms[i]); const struct tg_ring *ring = (struct tg_ring*)line; if (i > 0) write_char(wr, ','); write_char(wr, '('); switch (tg_geom_dims(geom)) { case 3: n = write_ring_points_wkt_3(wr, ring, pcoords, ncoords); ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; break; case 4: n = write_ring_points_wkt_4(wr, ring, pcoords, ncoords); ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); break; default: // 2 write_ring_points_wkt(wr, ring); break; } write_char(wr, ')'); } write_char(wr, ')'); } static void write_geom_multipolygon_wkt(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "MULTIPOLYGON"); write_zm_def_wkt(wr, geom); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY || !geom->multi || !geom->multi->ngeoms) { write_string(wr, " EMPTY"); return; } write_char(wr, '('); double *pcoords = (double *)geom->coords; int ncoords = geom->ncoords; int n; for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_poly *poly = tg_geom_poly( geom->multi->geoms[i]); if (i > 0) write_char(wr, ','); write_char(wr, '('); switch (tg_geom_dims(geom)) { case 3: n = write_poly_points_wkt_3(wr, poly, pcoords, ncoords); ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; break; case 4: n = write_poly_points_wkt_4(wr, poly, pcoords, ncoords); ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); break; default: // 2 write_poly_points_wkt(wr, poly); break; } write_char(wr, ')'); } write_char(wr, ')'); } static void write_geom_wkt(const struct tg_geom *geom, struct writer *wr); static void write_geom_geometrycollection_wkt(const struct tg_geom *geom, struct writer *wr) { write_string(wr, "GEOMETRYCOLLECTION"); write_zm_def_wkt(wr, geom); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY || !geom->multi || !geom->multi->ngeoms) { write_string(wr, " EMPTY"); return; } write_char(wr, '('); int ngeoms = tg_geom_num_geometries(geom); for (int i = 0; i < ngeoms; i++) { if (i > 0) write_char(wr, ','); write_geom_wkt(tg_geom_geometry_at(geom, i), wr); } write_char(wr, ')'); } static void write_base_geom_wkt(const struct tg_geom *geom, struct writer *wr) { switch (gettype(geom)) { case TG_POINT: write_geom_point_wkt(geom, wr); break; case TG_LINESTRING: write_geom_linestring_wkt(geom, wr); break; case TG_POLYGON: write_geom_polygon_wkt(geom, wr); break; case TG_MULTIPOINT: write_geom_multipoint_wkt(geom, wr); break; case TG_MULTILINESTRING: write_geom_multilinestring_wkt(geom, wr); break; case TG_MULTIPOLYGON: write_geom_multipolygon_wkt(geom, wr); break; case TG_GEOMETRYCOLLECTION: write_geom_geometrycollection_wkt(geom, wr); break; } } static void write_geom_wkt(const struct tg_geom *geom, struct writer *wr) { switch (getbase(geom)) { case BASE_GEOM: write_base_geom_wkt(geom, wr); break; case BASE_POINT: write_point_wkt((struct boxed_point*)geom, wr); break; case BASE_LINE: write_line_wkt((struct tg_line*)geom, wr); break; case BASE_RING: write_ring_wkt((struct tg_ring*)geom, wr); break; case BASE_POLY: write_poly_wkt((struct tg_poly*)geom, wr); break; } } /// Writes a Well-known text (WKT) representation of a geometry. /// /// The content is stored as a C string in the buffer pointed to by dst. /// A terminating null character is automatically appended after the /// content written. /// /// @param geom Input geometry /// @param dst Buffer where the resulting content is stored. /// @param n Maximum number of bytes to be used in the buffer. /// @return The number of characters, not including the null-terminator, /// needed to store the content into the C string buffer. /// If the returned length is greater than n-1, then only a partial copy /// occurred, for example: /// /// ``` /// char str[64]; /// size_t len = tg_geom_wkt(geom, str, sizeof(str)); /// if (len > sizeof(str)-1) { /// // ... write did not complete ... /// } /// ``` /// /// @see tg_geom_geojson() /// @see tg_geom_wkb() /// @see tg_geom_hex() /// @see GeometryWriting size_t tg_geom_wkt(const struct tg_geom *geom, char *dst, size_t n) { if (!geom) return 0; struct writer wr = { .dst = (uint8_t*)dst, .n = n }; write_geom_wkt(geom, &wr); write_nullterm(&wr); return wr.count; } // wkb static const char *wkb_invalid_child_type(void) { return "invalid child type"; } static uint32_t read_uint32(const uint8_t *data, bool swap) { uint32_t x; memcpy(&x, data, sizeof(uint32_t)); return swap ? __builtin_bswap32(x) : x; } static uint64_t read_uint64(const uint8_t *data, bool swap) { uint64_t x; memcpy(&x, data, sizeof(uint64_t)); return swap ? __builtin_bswap64(x) : x; } static double read_double(const uint8_t *data, bool swap) { return ((union raw_double){.u=read_uint64(data, swap)}).d; } #define read_uint32(name) { \ if (i+4 > len) goto invalid; \ uint32_t _val_ = read_uint32(wkb+i, swap); \ i += 4; \ (name) = _val_; \ } #define read_posn(posn) { \ if (i+(8*dims) > len) goto invalid; \ for (int j = 0; j < dims; j++) { \ (posn)[j] = read_double(wkb+i, swap); \ i += 8; \ } \ } static const char *wkb_invalid_err(void) { return "invalid binary"; } #define PARSE_FAIL SIZE_MAX // returns the updated wkb index. static size_t parse_wkb_posns(enum base base, int dims, const uint8_t *wkb, size_t len, size_t i, bool swap, struct dvec *posns, struct dvec *xcoords, struct tg_point **points, int *npoints, const char **err) { *err = NULL; double posn[4]; uint32_t count; read_uint32(count); if (count == 0) return i; if (dims == 2 && !swap && len-i >= count*2*8) { #if !defined(__x86_64__) && !defined(__aarch64__) if (((uintptr_t)(wkb+i))&7) { // Must load doubles into an array with an 8-byte boundary. size_t cap = posns->cap; cap = cap == 0 ? 2 : cap; while (cap < count*2) { cap *= 2; } double *data = tg_realloc(posns->data, cap*8); if (!data) { return PARSE_FAIL; } memcpy(data, wkb+i, count*2*8); posns->data = data; posns->cap = cap; posns->len = count/2; *points = (void*)posns->data; } else #endif { // Use the point data directly. No allocations. *points = (void*)(wkb+i); } *npoints = count; i += count*2*8; } else { for (uint32_t j = 0 ; j < count; j++) { read_posn(posn); for (int i = 0; i < 2; i++) { if (!dvec_append(posns, posn[i])) { return PARSE_FAIL; } } for (int i = 2; i < dims; i++) { if (!dvec_append(xcoords, posn[i])) { return PARSE_FAIL; } } } *points = (void*)posns->data; *npoints = posns->len / 2; } if (!check_parse_posns(base, (void*)(*points), (*npoints) * 2, err)) { return PARSE_FAIL; } return i; invalid: *err = wkb_invalid_err(); return PARSE_FAIL; } static size_t parse_wkb_multi_posns(enum base base, int dims, const uint8_t *wkb, size_t len, size_t i, bool swap, struct dvec *posns, struct rvec *rings, struct tg_poly **poly, struct dvec *xcoords, enum tg_index ix, const char **err) { *err = NULL; uint32_t count; read_uint32(count); if (count == 0) return i; for (uint32_t j = 0 ; j < count; j++) { struct tg_point *points = NULL; int npoints = 0; posns->len = 0; i = parse_wkb_posns(base, dims, wkb, len, i, swap, posns, xcoords, &points, &npoints, err); if (i == PARSE_FAIL) return PARSE_FAIL; struct tg_ring *ring = tg_ring_new_ix(points, npoints, ix); if (!ring) return PARSE_FAIL; if (!rvec_append(rings, ring)) { tg_ring_free(ring); return PARSE_FAIL; } } *poly = tg_poly_new(rings->data[0], (struct tg_ring const*const*)rings->data+1, rings->len-1); if (!*poly) return PARSE_FAIL; for (size_t i = 0; i < rings->len; i++) { tg_ring_free(rings->data[i]); } rings->len = 0; return i; invalid: *err = wkb_invalid_err(); return PARSE_FAIL; } static size_t parse_wkb_point(const uint8_t *wkb, size_t len, size_t i, bool swap, bool z, bool m, int depth, enum tg_index ix, struct tg_geom **gout) { (void)depth; (void)ix; int dims = z ? m ? 4 : 3 : m ? 3 : 2; double posn[4]; read_posn(posn); struct tg_geom *geom = NULL; if (isnan(posn[0])) { bool empty = true; for (int i = 1; i < dims; i++) { if (!isnan(posn[i])) { empty = false; break; } } if (empty) { geom = tg_geom_new_point_empty(); *gout = geom; return i; } } struct tg_point pt = { posn[0], posn[1] }; if (dims == 2) { geom = tg_geom_new_point(pt); } else if (dims == 3) { if (m) { geom = tg_geom_new_point_m(pt, posn[2]); } else { geom = tg_geom_new_point_z(pt, posn[2]); } } else { geom = tg_geom_new_point_zm(pt, posn[2], posn[3]); } *gout = geom; return i; invalid: *gout = make_parse_error(wkb_invalid_err()); return PARSE_FAIL; } static size_t parse_wkb_linestring(const uint8_t *wkb, size_t len, size_t i, bool swap, bool z, bool m, int depth, enum tg_index ix, struct tg_geom **gout) { (void)depth; struct tg_geom *gerr = NULL; struct tg_geom *geom = NULL; struct tg_line *line = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct tg_point *points = NULL; int npoints = 0; const char *err = NULL; int dims = z ? m ? 4 : 3 : m ? 3 : 2; i = parse_wkb_posns(BASE_LINE, dims, wkb, len, i, swap, &posns, &xcoords, &points, &npoints, &err); if (i == PARSE_FAIL) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } if (npoints == 0) { geom = tg_geom_new_linestring_empty(); goto cleanup; } line = tg_line_new_ix(points, npoints, ix); if (!line) goto fail; switch (dims) { case 2: geom = tg_geom_new_linestring(line); break; case 3: if (m) { geom = tg_geom_new_linestring_m(line, xcoords.data, xcoords.len); } else { geom = tg_geom_new_linestring_z(line, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_linestring_zm(line, xcoords.data, xcoords.len); break; } cleanup: tg_line_free(line); if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); *gout = geom; return i; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; } static size_t parse_wkb_polygon(const uint8_t *wkb, size_t len, size_t i, bool swap, bool z, bool m, int depth, enum tg_index ix, struct tg_geom **gout) { (void)depth; struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct tg_poly *poly = NULL; struct rvec rings = { 0 }; const char *err = NULL; int dims = z ? m ? 4 : 3 : m ? 3 : 2; i = parse_wkb_multi_posns(BASE_RING, dims, wkb, len, i, swap, &posns, &rings, &poly, &xcoords, ix, &err); if (i == PARSE_FAIL) { gerr = err ? make_parse_error("%s", err) : NULL; goto fail; } if (!poly) { geom = tg_geom_new_polygon_empty(); goto cleanup; } switch (dims) { case 2: geom = tg_geom_new_polygon(poly); break; case 3: if (m) { geom = tg_geom_new_polygon_m(poly, xcoords.data, xcoords.len); } else { geom = tg_geom_new_polygon_z(poly, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_polygon_zm(poly, xcoords.data, xcoords.len); break; } cleanup: tg_poly_free(poly); if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); if (rings.data) { for (size_t i = 0; i < rings.len; i++) { tg_ring_free(rings.data[i]); } tg_free(rings.data); } *gout = geom; return i; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; } static size_t parse_wkb(const uint8_t *wkb, size_t len, size_t i, int depth, enum tg_index ix, struct tg_geom **g); static bool wkb_type_match(const struct tg_geom *child, enum tg_geom_type type, bool z, bool m) { bool child_has_z = (getflags(child)&HAS_Z) == HAS_Z; bool child_has_m = (getflags(child)&HAS_M) == HAS_M; return gettype(child) == type && child_has_z == z && child_has_m == m; } static size_t parse_wkb_multipoint(const uint8_t *wkb, size_t len, size_t i, bool swap, bool z, bool m, int depth, enum tg_index ix, struct tg_geom **gout) { int dims = z ? m ? 4 : 3 : m ? 3 : 2; struct dvec posns = { 0 }; struct dvec xcoords = { 0 }; struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct tg_geom *point = NULL; uint32_t count; read_uint32(count); for (size_t j = 0; j < count; j++) { i = parse_wkb(wkb, len, i, depth+1, ix, &point); if (!point || i == PARSE_FAIL || tg_geom_error(point)) { gerr = point; point = NULL; goto fail; } if (!wkb_type_match(point, TG_POINT, z, m)) { gerr = make_parse_error(wkb_invalid_child_type()); goto fail; } struct tg_point pt = tg_geom_point(point); if (!dvec_append(&posns, pt.x) || !dvec_append(&posns, pt.y)) goto fail; if (z || m) { if (z && m) { if (!dvec_append(&xcoords, tg_geom_z(point))|| !dvec_append(&xcoords, tg_geom_m(point))) { goto fail; } } else if (z) { if (!dvec_append(&xcoords, tg_geom_z(point))) goto fail; } else if (m) { if (!dvec_append(&xcoords, tg_geom_m(point))) goto fail; } } tg_geom_free(point); point = NULL; } const struct tg_point *points = (struct tg_point*)posns.data; int npoints = posns.len/2; switch (dims) { case 2: geom = tg_geom_new_multipoint(points, npoints); break; case 3: if (m) { geom = tg_geom_new_multipoint_m(points, npoints, xcoords.data, xcoords.len); } else { geom = tg_geom_new_multipoint_z(points, npoints, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_multipoint_zm(points, npoints, xcoords.data, xcoords.len); break; } cleanup: if (point) tg_geom_free(point); if (posns.data) tg_free(posns.data); if (xcoords.data) tg_free(xcoords.data); *gout = geom; return i; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; invalid: gerr = make_parse_error(wkb_invalid_err()); goto fail; } static size_t parse_wkb_multilinestring(const uint8_t *wkb, size_t len, size_t i, bool swap, bool z, bool m, int depth, enum tg_index ix, struct tg_geom **gout) { int dims = z ? m ? 4 : 3 : m ? 3 : 2; struct dvec xcoords = { 0 }; struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct tg_geom *child = NULL; struct lvec lines = { 0 }; uint32_t count; read_uint32(count); for (size_t j = 0; j < count; j++) { i = parse_wkb(wkb, len, i, depth+1, ix, &child); if (!child || i == PARSE_FAIL || tg_geom_error(child)) { gerr = child; child = NULL; goto fail; } if (!wkb_type_match(child, TG_LINESTRING, z, m)) { gerr = make_parse_error(wkb_invalid_child_type()); goto fail; } struct tg_line *line = tg_line_clone(tg_geom_line(child)); if (!lvec_append(&lines, line)) { tg_line_free(line); goto fail; } const double *coords = tg_geom_extra_coords(child); int ncoords = tg_geom_num_extra_coords(child); for (int i = 0; i < ncoords; i++) { if (!dvec_append(&xcoords, coords[i])) goto fail; } tg_geom_free(child); child = NULL; } switch (dims) { case 2: geom = tg_geom_new_multilinestring( (struct tg_line const*const*)lines.data, lines.len); break; case 3: if (m) { geom = tg_geom_new_multilinestring_m( (struct tg_line const*const*)lines.data, lines.len, xcoords.data, xcoords.len); } else { geom = tg_geom_new_multilinestring_z( (struct tg_line const*const*)lines.data, lines.len, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_multilinestring_zm( (struct tg_line const*const*)lines.data, lines.len, xcoords.data, xcoords.len); break; } cleanup: if (child) tg_geom_free(child); if (xcoords.data) tg_free(xcoords.data); if (lines.data) { for (size_t i = 0; i < lines.len; i++) { tg_line_free(lines.data[i]); } tg_free(lines.data); } *gout = geom; return i; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; invalid: gerr = make_parse_error(wkb_invalid_err()); goto fail; } static size_t parse_wkb_multipolygon(const uint8_t *wkb, size_t len, size_t i, bool swap, bool z, bool m, int depth, enum tg_index ix, struct tg_geom **gout) { int dims = z ? m ? 4 : 3 : m ? 3 : 2; struct dvec xcoords = { 0 }; struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct tg_geom *child = NULL; struct pvec polys = { 0 }; uint32_t count; read_uint32(count); for (size_t j = 0; j < count; j++) { i = parse_wkb(wkb, len, i, depth+1, ix, &child); if (!child || i == PARSE_FAIL || tg_geom_error(child)) { gerr = child; child = NULL; goto fail; } if (!wkb_type_match(child, TG_POLYGON, z, m)) { gerr = make_parse_error(wkb_invalid_child_type()); goto fail; } struct tg_poly *poly = tg_poly_clone(tg_geom_poly(child)); if (!pvec_append(&polys, poly)) { tg_poly_free(poly); goto fail; } const double *coords = tg_geom_extra_coords(child); int ncoords = tg_geom_num_extra_coords(child); for (int i = 0; i < ncoords; i++) { if (!dvec_append(&xcoords, coords[i])) goto fail; } tg_geom_free(child); child = NULL; } switch (dims) { case 2: geom = tg_geom_new_multipolygon( (struct tg_poly const*const*)polys.data, polys.len); break; case 3: if (m) { geom = tg_geom_new_multipolygon_m( (struct tg_poly const*const*)polys.data, polys.len, xcoords.data, xcoords.len); } else { geom = tg_geom_new_multipolygon_z( (struct tg_poly const*const*)polys.data, polys.len, xcoords.data, xcoords.len); } break; default: geom = tg_geom_new_multipolygon_zm( (struct tg_poly const*const*)polys.data, polys.len, xcoords.data, xcoords.len); break; } cleanup: if (child) tg_geom_free(child); if (xcoords.data) tg_free(xcoords.data); if (polys.data) { for (size_t i = 0; i < polys.len; i++) { tg_poly_free(polys.data[i]); } tg_free(polys.data); } *gout = geom; return i; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; invalid: gerr = make_parse_error(wkb_invalid_err()); goto fail; } static size_t parse_wkb_geometrycollection(const uint8_t *wkb, size_t len, size_t i, bool swap, bool z, bool m, int depth, enum tg_index ix, struct tg_geom **gout) { (void)z; (void)m; // not used struct tg_geom *geom = NULL; struct tg_geom *gerr = NULL; struct gvec geoms = { 0 }; uint32_t count; read_uint32(count); for (size_t j = 0; j < count; j++) { struct tg_geom *child = NULL; i = parse_wkb(wkb, len, i, depth+1, ix, &child); if (!child || i == PARSE_FAIL || tg_geom_error(child)) { gerr = child; goto fail; } if (!gvec_append(&geoms, child)) { tg_geom_free(child); goto fail; } } geom = tg_geom_new_geometrycollection( (struct tg_geom const*const*)geoms.data, geoms.len); cleanup: if (geoms.data) { for (size_t i = 0; i < geoms.len; i++) { tg_geom_free(geoms.data[i]); } tg_free(geoms.data); } *gout = geom; return i; fail: tg_geom_free(geom); geom = gerr; gerr = NULL; goto cleanup; invalid: gerr = make_parse_error(wkb_invalid_err()); goto fail; } static size_t parse_wkb(const uint8_t *wkb, size_t len, size_t i, int depth, enum tg_index ix, struct tg_geom **g) { if (i == len) goto invalid; if (wkb[i] >> 1) goto invalid; // not 1 or 0 int d = depth; if (d > MAXDEPTH) goto invalid; // Set the 'swap' bool which indicates that the wkb numbers need swapping // to match the host endianness. #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ bool swap = wkb[i] == 1; #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ bool swap = wkb[i] == 0; #else #error "cannot determine byte order" #endif i++; uint32_t type; read_uint32(type); bool has_srid = !!(type & 0x20000000); type &= 0xFFFF; int srid = 0; if (has_srid) { // Read the SRID from the extended wkb format. uint32_t usrid; read_uint32(usrid); srid = (int)usrid; } (void)srid; // Now throw it away. bool s = swap; switch (type) { case 1: return parse_wkb_point(wkb, len, i, s, 0, 0, d, ix, g); case 1001: return parse_wkb_point(wkb, len, i, s, 1, 0, d, ix, g); case 2001: return parse_wkb_point(wkb, len, i, s, 0, 1, d, ix, g); case 3001: return parse_wkb_point(wkb, len, i, s, 1, 1, d, ix, g); case 2: return parse_wkb_linestring(wkb, len, i, s, 0, 0, d, ix, g); case 1002: return parse_wkb_linestring(wkb, len, i, s, 1, 0, d, ix, g); case 2002: return parse_wkb_linestring(wkb, len, i, s, 0, 1, d, ix, g); case 3002: return parse_wkb_linestring(wkb, len, i, s, 1, 1, d, ix, g); case 3: return parse_wkb_polygon(wkb, len, i, s, 0, 0, d, ix, g); case 1003: return parse_wkb_polygon(wkb, len, i, s, 1, 0, d, ix, g); case 2003: return parse_wkb_polygon(wkb, len, i, s, 0, 1, d, ix, g); case 3003: return parse_wkb_polygon(wkb, len, i, s, 1, 1, d, ix, g); case 4: return parse_wkb_multipoint(wkb, len, i, s, 0, 0, d, ix, g); case 1004: return parse_wkb_multipoint(wkb, len, i, s, 1, 0, d, ix, g); case 2004: return parse_wkb_multipoint(wkb, len, i, s, 0, 1, d, ix, g); case 3004: return parse_wkb_multipoint(wkb, len, i, s, 1, 1, d, ix, g); case 5: return parse_wkb_multilinestring(wkb, len, i, s, 0, 0, d, ix, g); case 1005: return parse_wkb_multilinestring(wkb, len, i, s, 1, 0, d, ix, g); case 2005: return parse_wkb_multilinestring(wkb, len, i, s, 0, 1, d, ix, g); case 3005: return parse_wkb_multilinestring(wkb, len, i, s, 1, 1, d, ix, g); case 6: return parse_wkb_multipolygon(wkb, len, i, s, 0, 0, d, ix, g); case 1006: return parse_wkb_multipolygon(wkb, len, i, s, 1, 0, d, ix, g); case 2006: return parse_wkb_multipolygon(wkb, len, i, s, 0, 1, d, ix, g); case 3006: return parse_wkb_multipolygon(wkb, len, i, s, 1, 1, d, ix, g); case 7: case 1007: case 2007: case 3007: return parse_wkb_geometrycollection(wkb, len, i, s, 0, 0, d, ix, g); default: *g = make_parse_error("invalid type"); return PARSE_FAIL; } invalid: *g = make_parse_error("invalid binary"); return PARSE_FAIL; } /// Parse Well-known binary (WKB). /// @param wkb WKB data /// @param len Length of data /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_wkb_ix() /// @see tg_geom_error() /// @see tg_geom_wkb() /// @see GeometryParsing struct tg_geom *tg_parse_wkb(const uint8_t *wkb, size_t len) { return tg_parse_wkb_ix(wkb, len, 0); } /// Parse Well-known binary (WKB) using provided indexing option. /// @param wkb WKB data /// @param len Length of data /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_wkb() struct tg_geom *tg_parse_wkb_ix(const uint8_t *wkb, size_t len, enum tg_index ix) { struct tg_geom *geom = NULL; parse_wkb(wkb, len, 0, 0, ix, &geom); if (!geom) return NULL; if ((getflags(geom)&IS_ERROR) == IS_ERROR) { struct tg_geom *gerr = make_parse_error("ParseError: %s", geom->error); tg_geom_free(geom); return gerr; } return geom; } static void write_wkb_type(struct writer *wr, const struct head *head) { uint32_t type = gettype(head); if ((getflags(head)&HAS_Z) == HAS_Z) { if ((getflags(head)&HAS_M) == HAS_M) { type += 3000; } else { type += 1000; } } else if ((getflags(head)&HAS_M) == HAS_M) { type += 2000; } write_byte(wr, 1); write_uint32le(wr, type); } static void write_posn_wkb(struct writer *wr, struct tg_point posn) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ if (wr->count+16 < wr->n) { memcpy(wr->dst+wr->count, &posn, 16); wr->count += 16; return; } #endif write_doublele(wr, posn.x); write_doublele(wr, posn.y); } static void write_posn_wkb_3(struct writer *wr, struct tg_point posn, double z) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ if (wr->count+24 < wr->n) { memcpy(wr->dst+wr->count, ((double[3]){posn.x, posn.y, z}), 24); wr->count += 24; return; } #endif write_doublele(wr, posn.x); write_doublele(wr, posn.y); write_doublele(wr, z); } static void write_posn_wkb_4(struct writer *wr, struct tg_point posn, double z, double m) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ if (wr->count+32 < wr->n) { memcpy(wr->dst+wr->count, ((double[4]){posn.x, posn.y, z, m}), 32); wr->count += 32; return; } #endif write_doublele(wr, posn.x); write_doublele(wr, posn.y); write_doublele(wr, z); write_doublele(wr, m); } static int write_ring_points_wkb(struct writer *wr, const struct tg_ring *ring) { write_uint32le(wr, ring->npoints); size_t needed = ring->npoints*16; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ if (wr->count+needed <= wr->n) { memcpy(wr->dst+wr->count, ring->points, needed); wr->count += needed; return ring->npoints; } #endif if (wr->count >= wr->n) { wr->count += needed; } else { for (int i = 0 ; i < ring->npoints; i++) { write_posn_wkb(wr, ring->points[i]); } } return ring->npoints; } static int write_ring_points_wkb_3(struct writer *wr, const struct tg_ring *ring, const double *coords, int ncoords) { write_uint32le(wr, ring->npoints); size_t needed = ring->npoints*24; if (wr->count >= wr->n) { wr->count += needed; } else { double z; int j = 0; for (int i = 0 ; i < ring->npoints; i++) { z = (j < ncoords) ? coords[j++] : 0; write_posn_wkb_3(wr, ring->points[i], z); } } return ring->npoints; } static int write_ring_points_wkb_4(struct writer *wr, const struct tg_ring *ring, const double *coords, int ncoords) { write_uint32le(wr, ring->npoints); size_t needed = ring->npoints*32; if (wr->count >= wr->n) { wr->count += needed; } else { double z, m; int j = 0; for (int i = 0 ; i < ring->npoints; i++) { z = (j < ncoords) ? coords[j++] : 0; m = (j < ncoords) ? coords[j++] : 0; write_posn_wkb_4(wr, ring->points[i], z, m); } } return ring->npoints; } static int write_poly_points_wkb(struct writer *wr, const struct tg_poly *poly) { int count = 0; int nholes = tg_poly_num_holes(poly); write_uint32le(wr, 1+nholes); write_ring_points_wkb(wr, tg_poly_exterior(poly)); for (int i = 0 ; i < nholes; i++) { count += write_ring_points_wkb(wr, tg_poly_hole_at(poly, i)); } return count; } static int write_poly_points_wkb_3(struct writer *wr, const struct tg_poly *poly, const double *coords, int ncoords) { int count = 0; double *pcoords = (double*)coords; int nholes = tg_poly_num_holes(poly); write_uint32le(wr, 1+nholes); const struct tg_ring *exterior = tg_poly_exterior(poly); int n = write_ring_points_wkb_3(wr, exterior, pcoords, ncoords); count += n; ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; for (int i = 0 ; i < nholes; i++) { const struct tg_ring *hole = tg_poly_hole_at(poly, i); int n = write_ring_points_wkb_3(wr, hole, pcoords, ncoords); count += n; ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; } return count; } static int write_poly_points_wkb_4(struct writer *wr, const struct tg_poly *poly, const double *coords, int ncoords) { int count = 0; double *pcoords = (double*)coords; int nholes = tg_poly_num_holes(poly); write_uint32le(wr, 1+nholes); const struct tg_ring *exterior = tg_poly_exterior(poly); int n = write_ring_points_wkb_4(wr, exterior, pcoords, ncoords); count += n; ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); for (int i = 0 ; i < nholes; i++) { const struct tg_ring *hole = tg_poly_hole_at(poly, i); int n = write_ring_points_wkb_4(wr, hole, pcoords, ncoords); count += n; ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); } return count; } static void write_point_wkb(struct boxed_point *point, struct writer *wr) { write_wkb_type(wr, &point->head); write_posn_wkb(wr, point->point); } static void write_line_wkb(struct tg_line *line, struct writer *wr) { struct tg_ring *ring = (struct tg_ring*)line; write_wkb_type(wr, &ring->head); write_ring_points_wkb(wr, ring); } static void write_ring_wkb(struct tg_ring *ring, struct writer *wr) { (void)ring; (void)wr; write_wkb_type(wr, &ring->head); write_uint32le(wr, 1); write_ring_points_wkb(wr, ring); } static void write_poly_wkb(struct tg_poly *poly, struct writer *wr) { (void)poly; (void)wr; write_wkb_type(wr, &poly->head); write_poly_points_wkb(wr, poly); } static void write_geom_point_wkb(const struct tg_geom *geom, struct writer *wr) { write_wkb_type(wr, &geom->head); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_posn_wkb(wr, (struct tg_point){ NAN, NAN }); } else { if ((getflags(geom)&HAS_Z) == HAS_Z) { if ((getflags(geom)&HAS_M) == HAS_M) { write_posn_wkb_4(wr, geom->point, geom->z, geom->m); } else { write_posn_wkb_3(wr, geom->point, geom->z); } } else if ((getflags(geom)&HAS_M) == HAS_M) { write_posn_wkb_3(wr, geom->point, geom->m); } else { write_posn_wkb(wr, geom->point); } } } static void write_geom_linestring_wkb(const struct tg_geom *geom, struct writer *wr) { write_wkb_type(wr, &geom->head); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_uint32le(wr, 0); return; } switch (tg_geom_dims(geom)) { case 3: write_ring_points_wkb_3(wr, (struct tg_ring*)geom->line, geom->coords, geom->ncoords); break; case 4: write_ring_points_wkb_4(wr, (struct tg_ring*)geom->line, geom->coords, geom->ncoords); break; default: write_ring_points_wkb(wr, (struct tg_ring*)geom->line); break; } } static void write_geom_polygon_wkb(const struct tg_geom *geom, struct writer *wr) { write_wkb_type(wr, &geom->head); if ((getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_uint32le(wr, 0); return; } switch (tg_geom_dims(geom)) { case 3: write_poly_points_wkb_3(wr, geom->poly, geom->coords, geom->ncoords); break; case 4: write_poly_points_wkb_4(wr, geom->poly, geom->coords, geom->ncoords); break; default: // 2 write_poly_points_wkb(wr, geom->poly); break; } } static void write_geom_multipoint_wkb(const struct tg_geom *geom, struct writer *wr) { write_wkb_type(wr, &geom->head); if (!geom->multi || (getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_uint32le(wr, 0); return; } int dims = tg_geom_dims(geom); double z, m; double *coords = (double *)geom->coords; int ncoords = geom->ncoords; int j = 0; write_uint32le(wr, geom->multi->ngeoms); struct head head = { 0 }; settype(&head, TG_POINT); setflags(&head, getflags(geom)&(HAS_Z|HAS_M)); for (int i = 0; i < geom->multi->ngeoms; i++) { struct tg_point point = tg_geom_point(geom->multi->geoms[i]); write_wkb_type(wr, &head); switch (dims) { case 3: z = (j < ncoords) ? coords[j++] : 0; write_posn_wkb_3(wr, point, z); break; case 4: z = (j < ncoords) ? coords[j++] : 0; m = (j < ncoords) ? coords[j++] : 0; write_posn_wkb_4(wr, point, z, m); break; default: // 2 write_posn_wkb(wr, point); break; } } } static void write_geom_multilinestring_wkb(const struct tg_geom *geom, struct writer *wr) { write_wkb_type(wr, &geom->head); if (!geom->multi || (getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_uint32le(wr, 0); return; } int dims = tg_geom_dims(geom); write_uint32le(wr, geom->multi->ngeoms); struct head head = { 0 }; settype(&head, TG_LINESTRING); setflags(&head, getflags(geom)&(HAS_Z|HAS_M)); const double *pcoords = geom->coords; int ncoords = geom->ncoords; int n; for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_line *line = tg_geom_line(geom->multi->geoms[i]); const struct tg_ring *ring = (struct tg_ring*)line; write_wkb_type(wr, &head); switch (dims) { case 3: n = write_ring_points_wkb_3(wr, ring, pcoords, ncoords); ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; break; case 4: n = write_ring_points_wkb_4(wr, ring, pcoords, ncoords); ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); break; default: // 2 write_ring_points_wkb(wr, ring); break; } } } static void write_geom_multipolygon_wkb(const struct tg_geom *geom, struct writer *wr) { write_wkb_type(wr, &geom->head); if (!geom->multi || (getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_uint32le(wr, 0); return; } int dims = tg_geom_dims(geom); write_uint32le(wr, geom->multi->ngeoms); struct head head = { 0 }; settype(&head, TG_POLYGON); setflags(&head, getflags(geom)&(HAS_Z|HAS_M)); const double *pcoords = geom->coords; int ncoords = geom->ncoords; int n; for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_poly *poly = tg_geom_poly(geom->multi->geoms[i]); write_wkb_type(wr, &head); switch (dims) { case 3: n = write_poly_points_wkb_3(wr, poly, pcoords, ncoords); ncoords -= n; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+n; break; case 4: n = write_poly_points_wkb_4(wr, poly, pcoords, ncoords); ncoords -= n*2; if (ncoords < 0) ncoords = 0; pcoords = ncoords == 0 ? NULL : pcoords+(n*2); break; default: // 2 write_poly_points_wkb(wr, poly); break; } } } static void write_geom_wkb(const struct tg_geom *geom, struct writer *wr); static void write_geom_geometrycollection_wkb(const struct tg_geom *geom, struct writer *wr) { write_wkb_type(wr, &geom->head); if (!geom->multi || (getflags(geom)&IS_EMPTY) == IS_EMPTY) { write_uint32le(wr, 0); return; } write_uint32le(wr, geom->multi->ngeoms); for (int i = 0; i < geom->multi->ngeoms; i++) { write_geom_wkb(geom->multi->geoms[i], wr); } } static void write_base_geom_wkb(const struct tg_geom *geom, struct writer *wr) { switch (gettype(geom)) { case TG_POINT: write_geom_point_wkb(geom, wr); break; case TG_LINESTRING: write_geom_linestring_wkb(geom, wr); break; case TG_POLYGON: write_geom_polygon_wkb(geom, wr); break; case TG_MULTIPOINT: write_geom_multipoint_wkb(geom, wr); break; case TG_MULTILINESTRING: write_geom_multilinestring_wkb(geom, wr); break; case TG_MULTIPOLYGON: write_geom_multipolygon_wkb(geom, wr); break; case TG_GEOMETRYCOLLECTION: write_geom_geometrycollection_wkb(geom, wr); break; } } static void write_geom_wkb(const struct tg_geom *geom, struct writer *wr) { switch (getbase(geom)) { case BASE_GEOM: write_base_geom_wkb(geom, wr); break; case BASE_POINT: write_point_wkb((struct boxed_point*)geom, wr); break; case BASE_LINE: write_line_wkb((struct tg_line*)geom, wr); break; case BASE_RING: write_ring_wkb((struct tg_ring*)geom, wr); break; case BASE_POLY: write_poly_wkb((struct tg_poly*)geom, wr); break; } } /// Writes a Well-known binary (WKB) representation of a geometry. /// /// The content is stored in the buffer pointed by dst. /// /// @param geom Input geometry /// @param dst Buffer where the resulting content is stored. /// @param n Maximum number of bytes to be used in the buffer. /// @return The number of characters needed to store the content into the /// buffer. /// If the returned length is greater than n, then only a partial copy /// occurred, for example: /// /// ``` /// uint8_t buf[64]; /// size_t len = tg_geom_wkb(geom, buf, sizeof(buf)); /// if (len > sizeof(buf)) { /// // ... write did not complete ... /// } /// ``` /// /// @see tg_geom_geojson() /// @see tg_geom_wkt() /// @see tg_geom_hex() /// @see GeometryWriting size_t tg_geom_wkb(const struct tg_geom *geom, uint8_t *dst, size_t n) { if (!geom) return 0; struct writer wr = { .dst = dst, .n = n }; write_geom_wkb(geom, &wr); return wr.count; } /// Writes a hex encoded Well-known binary (WKB) representation of a geometry. /// /// The content is stored as a C string in the buffer pointed to by dst. /// A terminating null character is automatically appended after the /// content written. /// /// @param geom Input geometry /// @param dst Buffer where the resulting content is stored. /// @param n Maximum number of bytes to be used in the buffer. /// @return The number of characters, not including the null-terminator, /// needed to store the content into the C string buffer. /// If the returned length is greater than n-1, then only a partial copy /// occurred, for example: /// /// ``` /// char str[64]; /// size_t len = tg_geom_hex(geom, str, sizeof(str)); /// if (len > sizeof(str)-1) { /// // ... write did not complete ... /// } /// ``` /// /// @see tg_geom_geojson() /// @see tg_geom_wkt() /// @see tg_geom_wkb() /// @see GeometryWriting size_t tg_geom_hex(const struct tg_geom *geom, char *dst, size_t n) { // Geom to hex is done by first writing as wkb, then rewrite as hex. // This is done by scanning the wkb in reverse, overwriting the data // along the way. static const uint8_t hexchars[] = "0123456789ABCDEF"; size_t count = tg_geom_wkb(geom, (uint8_t*)dst, n); if (count == 0) { if (n > 0) dst[0] = '\0'; return 0; } size_t i = count - 1; size_t j = count*2 - 1; while (1) { if (i < n) { uint8_t ch = dst[i]; if (j < n) dst[j] = hexchars[ch&15]; if (j-1 < n) dst[j-1] = hexchars[(ch>>4)&15]; } if (i == 0) break; i -= 1; j -= 2; } if (count*2 < n) dst[count*2] = '\0'; else if (n > 0) dst[n-1] = '\0'; return count*2; } static size_t parse_geobin(const uint8_t *geobin, size_t len, size_t i, size_t depth, enum tg_index ix, struct tg_geom **g); static struct tg_geom *parse_hex(const char *hex, size_t len, enum tg_index ix) { const uint8_t _ = 0; static const uint8_t hextoks[256] = { _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_, _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,01,2,3,4,5,6,7,8,9,10,_,_,_,_,_, _,_,11,12,13,14,15,16,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_, _,_,_,_,_,11,12,13,14,15,16,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_, }; uint8_t *dst = NULL; bool must_free = false; if (len == 0 || (len&1) == 1) goto invalid; uint8_t smallfry[128]; if (len/2 > sizeof(smallfry)) { dst = tg_malloc(len/2); if (!dst) return NULL; must_free = true; } else { dst = smallfry; } size_t j = 0; for (size_t i = 0; i < len; i += 2) { uint8_t b0 = hextoks[(uint8_t)hex[i+0]]; uint8_t b1 = hextoks[(uint8_t)hex[i+1]]; if (b0 == _ || b1 == _) goto invalid; dst[j] = ((b0-1)<<4)|(b1-1); j++; } struct tg_geom *geom; len /= 2; size_t n; if (len > 0 && dst[0] >= 0x2 && dst[0] <= 0x4) { n = parse_geobin(dst, len, 0, 0, ix, &geom); } else { n = parse_wkb(dst, len, 0, 0, ix, &geom); } (void)n; if (must_free) tg_free(dst); return geom; invalid: if (must_free) tg_free(dst); return make_parse_error(wkb_invalid_err()); } /// Parse hex encoded Well-known binary (WKB) or GeoBIN using provided indexing /// option. /// @param hex Hex data /// @param len Length of data /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_hex() /// @see tg_parse_hex_ix() /// @see GeometryParsing struct tg_geom *tg_parse_hexn_ix(const char *hex, size_t len, enum tg_index ix) { struct tg_geom *geom = parse_hex(hex, len, ix); if (!geom) return NULL; if ((getflags(geom)&IS_ERROR) == IS_ERROR) { struct tg_geom *gerr = make_parse_error("ParseError: %s", geom->error); tg_geom_free(geom); return gerr; } return geom; } /// Parse hex encoded Well-known binary (WKB) or GeoBIN using provided indexing /// option. /// @param hex Hex string. Must be null-terminated /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_hex() /// @see tg_parse_hexn_ix() /// @see GeometryParsing struct tg_geom *tg_parse_hex_ix(const char *hex, enum tg_index ix) { return tg_parse_hexn_ix(hex, hex?strlen(hex):0, ix); } /// Parse hex encoded Well-known binary (WKB) or GeoBIN with an included data /// length. /// @param hex Hex data /// @param len Length of data /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_hex() /// @see GeometryParsing struct tg_geom *tg_parse_hexn(const char *hex, size_t len) { return tg_parse_hexn_ix(hex, len, TG_DEFAULT); } /// Parse hex encoded Well-known binary (WKB) or GeoBIN. /// @param hex A hex string. Must be null-terminated /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_hexn() /// @see tg_parse_hex_ix() /// @see tg_parse_hexn_ix() /// @see tg_geom_error() /// @see tg_geom_hex() /// @see GeometryParsing struct tg_geom *tg_parse_hex(const char *hex) { return tg_parse_hexn_ix(hex, hex?strlen(hex):0, TG_DEFAULT); } /// Calculate the area of a ring. double tg_ring_area(const struct tg_ring *ring) { if (tg_ring_empty(ring)) return 0; // The ring area has already been calculated by process_points. return ring->area; } /// Calculate the perimeter length of a ring. double tg_ring_perimeter(const struct tg_ring *ring) { if (tg_ring_empty(ring)) return 0; int nsegs = tg_ring_num_segments(ring); double perim = 0; for (int i = 0; i < nsegs; i++) { struct tg_point a = ring->points[i]; struct tg_point b = ring->points[i+1]; perim += length(a.x, a.y, b.x, b.y); } return perim; } double tg_ring_polsby_popper_score(const struct tg_ring *ring) { // Calculate the polsby-popper score for the ring or line // https://en.wikipedia.org/wiki/Polsby–Popper_test // // The score is calculated by multiplying the polygon area by 4pi // and dividing by the perimeter squared. A perfect circle has a score of 1 // and all other shapes will be smaller. Itty bitty scores mean the // polygon is really something nuts or has bad data. double score = 0.0; double perim = tg_ring_perimeter(ring); double area = tg_ring_area(ring); if (perim > 0) { score = (area * M_PI * 4) / (perim * perim); } return score; } double tg_line_polsby_popper_score(const struct tg_line *line) { const struct tg_ring *ring = (const struct tg_ring*)line; return tg_ring_polsby_popper_score(ring); } struct tg_ring *tg_circle_new_ix(struct tg_point center, double radius, int steps, enum tg_index ix) { steps--; radius = radius < 0 ? 0 : radius; steps = steps < 3 ? 3 : steps; struct tg_ring *ring = NULL; struct tg_point *points = tg_malloc(sizeof(struct tg_point)*(steps+1)); if (points) { int i = 0; for (double th = 0.0; th <= 360.0; th += 360.0 / (double)steps) { double radians = (M_PI / 180.0) * th; double x = center.x + radius * cos(radians); double y = center.y + radius * sin(radians); points[i] = (struct tg_point) { x, y }; i++; } // add last connecting point, make a total of steps points[steps] = points[0]; ring = tg_ring_new_ix(points, steps+1, ix); tg_free(points); } return ring; } struct tg_ring *tg_circle_new(struct tg_point center, double radius, int steps){ return tg_circle_new_ix(center, radius, steps, 0); } double tg_point_distance_segment(struct tg_point p, struct tg_segment s) { double a = p.x - s.a.x; double b = p.y - s.a.y; double c = s.b.x - s.a.x; double d = s.b.y - s.a.y; double e = c * c + d * d; double f = e ? (a * c + b * d) / e : 0.0; double g = fclamp0(f, 0, 1); double dx = p.x - (s.a.x + g * c); double dy = p.y - (s.a.y + g * d); return sqrt(dx * dx + dy * dy); } double tg_rect_distance_rect(struct tg_rect a, struct tg_rect b) { double dx = fmax0(fmax0(a.min.x, b.min.x) - fmin0(a.max.x, b.max.x), 0); double dy = fmax0(fmax0(a.min.y, b.min.y) - fmin0(a.max.y, b.max.y), 0); return sqrt(dx * dx + dy * dy); } double tg_point_distance_rect(struct tg_point p, struct tg_rect r) { return tg_rect_distance_rect((struct tg_rect) { p, p }, r); } double tg_point_distance_point(struct tg_point a, struct tg_point b) { return sqrt((a.x-b.x) * (a.x-b.x) + (a.y-b.y) * (a.y-b.y)); } enum nqentry_kind { NQUEUE_KIND_SEGMENT, NQUEUE_KIND_RECT }; struct nqentry { double dist; enum nqentry_kind kind:8; int rect_level:8; int more; union { int seg_index; int rect_index; }; }; // nqueue is a priority queue using a binary heap type structure for ordering // segments needed by the tg_ring_nearest function. struct nqueue { bool oom; // nqueue operation failure bool onheap; // items array is on the heap struct nqentry spare; struct nqentry *items; size_t len; size_t cap; size_t maxlen; }; #define NQUEUE_INIT(queue, init_cap) \ struct nqentry init_items[init_cap]; \ queue = (struct nqueue) { \ .cap = (init_cap), \ .items = init_items \ }; \ #define NQUEUE_DESTROY(queue) \ if (queue.onheap) { \ tg_free(queue.items); \ } \ static void nqswap(struct nqueue *queue, size_t i, size_t j) { queue->spare = queue->items[i]; queue->items[i] = queue->items[j]; queue->items[j] = queue->spare; } static int nqcompare(struct nqueue *queue, size_t i, size_t j) { const struct nqentry *a = &queue->items[i]; const struct nqentry *b = &queue->items[j]; return a->dist < b->dist ? -1 : a->dist > b->dist; } static void nqueue_push(struct nqueue *queue, const struct nqentry *item) { if (queue->oom) { return; } size_t elsize = sizeof(struct nqentry); if (queue->len == queue->cap) { size_t cap = queue->cap < 1000 ? queue->cap*2 : queue->cap*1.25; struct nqentry *items; if (!queue->onheap) { items = tg_malloc(elsize*cap); if (!items) goto oom; memcpy(items, queue->items, elsize*queue->len); } else { items = tg_realloc(queue->items, elsize*cap); if (!items) goto oom; } queue->items = items; queue->cap = cap; queue->onheap = true; } queue->items[queue->len++] = *item; size_t i = queue->len - 1; while (i != 0) { size_t parent = (i - 1) / 2; if (!(nqcompare(queue, parent, i) > 0)) break; nqswap(queue, parent, i); i = parent; } queue->maxlen = fmax0(queue->maxlen, queue->len); return; oom: queue->oom = true; } static const struct nqentry *nqueue_pop(struct nqueue *queue) { if (queue->len == 0 || queue->oom) { return NULL; } nqswap(queue, 0, queue->len-1); queue->len--; const struct nqentry *item = &queue->items[queue->len]; size_t i = 0; while (1) { size_t smallest = i; size_t left = i * 2 + 1; size_t right = i * 2 + 2; if (left < queue->len && nqcompare(queue, left, smallest) <= 0) { smallest = left; } if (right < queue->len && nqcompare(queue, right, smallest) <= 0) { smallest = right; } if (smallest == i) { break; } nqswap(queue, smallest, i); i = smallest; } return item; } /// Iterates over segments from nearest to farthest. /// /// This is a kNN operation. /// The caller must provide their own "rect_dist" and "seg_dist" callbacks to /// do the actual distance calculations. /// /// @param ring Input ring /// @param rect_dist Callback that returns the distance to a tg_rect. /// @param seg_dist Callback that returns the distance to a tg_segment. /// @param iter Callback that returns each segment in the ring in order of /// nearest to farthest. Caller must return true to continue to the next /// segment, or return false to stop iterating. /// @param udata User-defined data /// @return True if operation succeeded, false if out of memory. /// @note Though not typical, this operation may need to allocate memory. /// It's recommended to check the return value for success. /// @note The `*more` argument is an optional ref-value that is used for /// performing partial step-based or probability-based calculations. A detailed /// description of its use is outside the scope of this document. Ignoring it /// altogether is the preferred behavior. /// @see RingFuncs bool tg_ring_nearest_segment(const struct tg_ring *ring, double (*rect_dist)(struct tg_rect rect, int *more, void *udata), double (*seg_dist)(struct tg_segment seg, int *more, void *udata), bool (*iter)(struct tg_segment seg, double dist, int index, void *udata), void *udata) { if (!ring || !seg_dist) return true; struct nqueue queue; NQUEUE_INIT(queue, 256); struct index *ix = ring->index; int ixspread = ix ? ring->index->spread : 0; if (rect_dist && ix) { // Gather root rectangles for (int i = 0; i < ix->levels[0].nrects; i++) { int more = 0; struct tg_rect rect; ixrect_to_tg_rect(&ix->levels[0].rects[i], &rect); double dist = rect_dist(rect, &more, udata); struct nqentry entry = { .kind = NQUEUE_KIND_RECT, .dist = dist, .more = more, .rect_level = 0, .rect_index = i, }; nqueue_push(&queue, &entry); } } else { // Gather all segments for (int i = 0; i < ring->nsegs; i++) { struct tg_segment seg = { ring->points[i+0], ring->points[i+1] }; int more = 0; double dist = seg_dist(seg, &more, udata); struct nqentry entry = { .kind = NQUEUE_KIND_SEGMENT, .dist = dist, .more = more, .seg_index = i, }; nqueue_push(&queue, &entry); } } while (1) { const struct nqentry *ientry = nqueue_pop(&queue); if (!ientry) break; if (ientry->kind == NQUEUE_KIND_SEGMENT) { struct tg_segment seg = { ring->points[ientry->seg_index+0], ring->points[ientry->seg_index+1], }; if (ientry->more) { // Reinsert the segment struct nqentry entry = *ientry; entry.dist = seg_dist(seg, &entry.more, udata); nqueue_push(&queue, &entry); } else { // Segments are sent back to the caller. if (!iter(seg, ientry->dist, ientry->seg_index, udata)) { break; } } continue; } if (ientry->more) { // Reinsert the rectangle struct tg_rect rect; ixrect_to_tg_rect(&ix->levels[ientry->rect_level] .rects[ientry->rect_index], &rect); struct nqentry entry = *ientry; entry.dist = rect_dist(rect, &entry.more, udata); nqueue_push(&queue, &entry); continue; } int lvl = ientry->rect_level + 1; int start = ientry->rect_index*ixspread; ientry = NULL; // no longer need this if (lvl == ix->nlevels) { // Gather leaf segments int nsegs = ring->nsegs; int i = start; int e = i+ixspread; if (e > nsegs) e = nsegs; for (; i < e; i++) { struct tg_segment seg = { ring->points[i+0], ring->points[i+1] }; int more = 0; double dist = seg_dist(seg, &more, udata); struct nqentry entry = { .more = more, .dist = dist, .kind = NQUEUE_KIND_SEGMENT, .seg_index = i, }; nqueue_push(&queue, &entry); }; } else { // Gather branch rectangles const struct level *level = &ix->levels[lvl]; int i = start; int e = i+ixspread; if (e > level->nrects) e = level->nrects; for (; i < e; i++) { int more = 0; struct tg_rect rect; ixrect_to_tg_rect(&level->rects[i], &rect); double dist = rect_dist(rect, &more, udata); struct nqentry entry = { .more = more, .dist = dist, .kind = NQUEUE_KIND_RECT, .rect_level = lvl, .rect_index = i, }; nqueue_push(&queue, &entry); }; } } bool oom = queue.oom; NQUEUE_DESTROY(queue); return !oom; } /// Iterates over segments from nearest to farthest. /// @see tg_ring_nearest_segment(), which shares the same interface, for a /// detailed description. /// @see LineFuncs bool tg_line_nearest_segment(const struct tg_line *line, double (*rect_dist)(struct tg_rect rect, int *more, void *udata), double (*seg_dist)(struct tg_segment seg, int *more, void *udata), bool (*iter)(struct tg_segment seg, double dist, int index, void *udata), void *udata) { struct tg_ring *ring = (struct tg_ring *)line; return tg_ring_nearest_segment(ring, rect_dist, seg_dist, iter, udata); } //////////////////////////////////////////////////////////////////////////////// // Spatial predicates //////////////////////////////////////////////////////////////////////////////// /// Tests whether two geometries are topologically equal. /// @see GeometryPredicates bool tg_geom_equals(const struct tg_geom *a, const struct tg_geom *b) { return tg_geom_within(a, b) && tg_geom_contains(a, b); } /// Tests whether 'a' is fully contained inside of 'b'. /// @note Works the same as `tg_geom_covers(b, a)` /// @see GeometryPredicates bool tg_geom_coveredby(const struct tg_geom *a, const struct tg_geom *b) { return tg_geom_covers(b, a); } /// Tests whether 'a' and 'b' have no point in common, and are fully /// disconnected geometries. /// @note Works the same as `!tg_geom_intersects(a, b)` /// @see GeometryPredicates bool tg_geom_disjoint(const struct tg_geom *a, const struct tg_geom *b) { return !tg_geom_intersects(a, b); } /// Tests whether 'a' is contained inside of 'b' and not touching the boundary /// of 'b'. /// @note Works the same as `tg_geom_contains(b, a)` /// @warning This predicate returns **false** when geometry 'a' is *on* or /// *touching* the boundary of geometry 'b'. Such as when a point is on the /// edge of a polygon. /// For full coverage, consider using @ref tg_geom_coveredby. /// @see GeometryPredicates bool tg_geom_within(const struct tg_geom *a, const struct tg_geom *b) { return tg_geom_contains(b, a); } bool tg_geom_crosses(const struct tg_geom *a, const struct tg_geom *b) { (void)a; (void)b; // unsupported return false; } bool tg_geom_overlaps(const struct tg_geom *a, const struct tg_geom *b) { (void)a; (void)b; // unsupported return false; } int tg_geom_de9im_dims(const struct tg_geom *geom) { int dims = -1; if (geom) { switch (getbase(geom)) { case BASE_POINT: return 0; case BASE_LINE: return 1; case BASE_RING: return 2; case BASE_POLY: return 2; case BASE_GEOM: switch (gettype(geom)) { case TG_POINT: return 0; case TG_LINESTRING: return 1; case TG_POLYGON: return 2; case TG_MULTIPOINT: return 0; case TG_MULTILINESTRING: return 1; case TG_MULTIPOLYGON: return 2; case TG_GEOMETRYCOLLECTION: { int ngeoms = tg_geom_num_geometries(geom); for (int i = 0; i < ngeoms; i++) { const struct tg_geom *child = tg_geom_geometry_at(geom, i); int child_dims = tg_geom_de9im_dims(child); if (child_dims > dims) { dims = child_dims; } } }} } } return dims; } /// Copies a ring /// @param ring Input ring, caller retains ownership. /// @return A duplicate of the provided ring. /// @return NULL if out of memory /// @note The caller is responsible for freeing with tg_ring_free(). /// @note This method performs a deep copy of the entire geometry to new memory. /// @see RingFuncs struct tg_ring *tg_ring_copy(const struct tg_ring *ring) { if (!ring) { return NULL; } size_t size = ring_alloc_size(ring); struct tg_ring *ring2 = tg_malloc(size); if (!ring2) { return NULL; } memcpy(ring2, ring, size); rc_init(&ring2->head.rc); rc_retain(&ring2->head.rc); setnoheap(ring2, 0); if (ring->ystripes) { ring2->ystripes = tg_malloc(ring->ystripes->memsz); if (!ring2->ystripes) { tg_free(ring2); return NULL; } memcpy(ring2->ystripes, ring->ystripes, ring->ystripes->memsz); } return ring2; } /// Copies a line /// @param line Input line, caller retains ownership. /// @return A duplicate of the provided line. /// @return NULL if out of memory /// @note The caller is responsible for freeing with tg_line_free(). /// @note This method performs a deep copy of the entire geometry to new memory. /// @see LineFuncs struct tg_line *tg_line_copy(const struct tg_line *line) { return (struct tg_line*)tg_ring_copy((struct tg_ring*)line); } /// Copies a polygon. /// @param poly Input polygon, caller retains ownership. /// @return A duplicate of the provided polygon. /// @return NULL if out of memory /// @note The caller is responsible for freeing with tg_poly_free(). /// @note This method performs a deep copy of the entire geometry to new memory. /// @see PolyFuncs struct tg_poly *tg_poly_copy(const struct tg_poly *poly) { if (!poly) { return NULL; } if (getbase(&poly->head) == BASE_RING) { return (struct tg_poly*)tg_ring_copy((struct tg_ring*)poly); } struct tg_poly *poly2 = tg_malloc(sizeof(struct tg_poly)); if (!poly2) { goto fail; } memset(poly2, 0, sizeof(struct tg_poly)); memcpy(&poly2->head, &poly->head, sizeof(struct head)); rc_init(&poly2->head.rc); rc_retain(&poly2->head.rc); setnoheap(poly2, 0); poly2->exterior = tg_ring_copy(poly->exterior); if (!poly2->exterior) { goto fail; } if (poly->nholes > 0) { poly2->holes = tg_malloc(sizeof(struct tg_ring*)*poly->nholes); if (!poly2->holes) { goto fail; } poly2->nholes = poly->nholes; memset(poly2->holes, 0, sizeof(struct tg_ring*)*poly2->nholes); for (int i = 0; i < poly2->nholes; i++) { poly2->holes[i] = tg_ring_copy(poly->holes[i]); if (!poly2->holes[i]) { goto fail; } } } return poly2; fail: tg_poly_free(poly2); return NULL; } static struct tg_geom *geom_copy(const struct tg_geom *geom) { struct tg_geom *geom2 = tg_malloc(sizeof(struct tg_geom)); if (!geom2) { return NULL; } memset(geom2, 0, sizeof(struct tg_geom)); memcpy(&geom2->head, &geom->head, sizeof(struct head)); rc_init(&geom2->head.rc); rc_retain(&geom2->head.rc); setnoheap(geom2, 0); switch (gettype(geom)) { case TG_POINT: geom2->point.x = geom->point.x; geom2->point.y = geom->point.y; geom2->z = geom->z; geom2->m = geom->m; break; case TG_LINESTRING: geom2->line = tg_line_copy(geom->line); if (!geom2->line) { goto fail; } break; case TG_POLYGON: geom2->poly = tg_poly_copy(geom->poly); if (!geom2->poly) { goto fail; } break; case TG_MULTIPOINT: case TG_MULTILINESTRING: case TG_MULTIPOLYGON: case TG_GEOMETRYCOLLECTION: if (geom->multi) { geom2->multi = tg_malloc(sizeof(struct multi)); if (!geom2->multi) { goto fail; } memset(geom2->multi, 0, sizeof(struct multi)); geom2->multi->rect = geom->multi->rect; if (geom->multi->geoms) { size_t gsize = sizeof(struct tg_geom*)*geom->multi->ngeoms; geom2->multi->geoms = tg_malloc(gsize); if (!geom2->multi->geoms) { goto fail; } memset(geom2->multi->geoms, 0, gsize); geom2->multi->ngeoms = geom->multi->ngeoms; for (int i = 0; i < geom->multi->ngeoms; i++) { const struct tg_geom *child = geom->multi->geoms[i]; geom2->multi->geoms[i] = tg_geom_copy(child); if (!geom2->multi->geoms[i]) { goto fail; } } } if (geom->multi->index) { geom2->multi->index = tg_malloc(geom->multi->index->memsz); if (!geom2->multi->index) { goto fail; } memcpy(geom2->multi->index, geom->multi->index, geom->multi->index->memsz); } if (geom->multi->ixgeoms) { geom2->multi->ixgeoms = tg_malloc( geom->multi->ngeoms*sizeof(int)); if (!geom2->multi->ixgeoms) { goto fail; } memcpy(geom2->multi->ixgeoms, geom->multi->ixgeoms, geom->multi->ngeoms*sizeof(int)); } } break; } if (gettype(geom) != TG_POINT && geom->coords) { geom2->coords = tg_malloc(sizeof(double)*geom->ncoords); if (!geom2->coords) { goto fail; } geom2->ncoords = geom->ncoords; memcpy(geom2->coords, geom->coords, sizeof(double)*geom->ncoords); } if (geom->error) { // error and xjson share the same memory, so this copy covers both. size_t esize = strlen(geom->error)+1; geom2->error = tg_malloc(esize); if (!geom2->error) { goto fail; } memcpy(geom2->error, geom->error, esize); } return geom2; fail: tg_geom_free(geom2); return NULL; } static struct boxed_point *boxed_point_copy(const struct boxed_point *point) { struct boxed_point *point2 = tg_malloc(sizeof(struct boxed_point)); if (!point2) { return NULL; } memcpy(point2, point, sizeof(struct boxed_point)); rc_init(&point2->head.rc); rc_retain(&point2->head.rc); setnoheap(point2, 0); return point2; } /// Copies a geometry /// @param geom Input geometry, caller retains ownership. /// @return A duplicate of the provided geometry. /// @return NULL if out of memory /// @note The caller is responsible for freeing with tg_geom_free(). /// @note This method performs a deep copy of the entire geometry to new memory. /// @see GeometryConstructors struct tg_geom *tg_geom_copy(const struct tg_geom *geom) { if (geom) { switch (getbase(geom)) { case BASE_GEOM: return geom_copy(geom); case BASE_POINT: return (struct tg_geom*)boxed_point_copy((struct boxed_point*)geom); case BASE_LINE: return (struct tg_geom*)tg_line_copy((struct tg_line*)geom); case BASE_RING: return (struct tg_geom*)tg_ring_copy((struct tg_ring*)geom); case BASE_POLY: return (struct tg_geom*)tg_poly_copy((struct tg_poly*)geom); } } return NULL; } /// Tests whether a geometry intersects a rect. /// @see GeometryPredicates bool tg_geom_intersects_rect(const struct tg_geom *a, struct tg_rect b) { struct tg_ring *ring = stack_ring(); rect_to_ring(b, ring); return tg_geom_intersects(a, (struct tg_geom*)ring); } static bool multi_index_search(const struct multi *multi, struct tg_rect rect, int levelidx, int startidx, bool (*iter)(const struct tg_geom *geom, int index, void *udata), void *udata) { const struct index *index = multi->index; if (levelidx == index->nlevels) { // leaf int s = startidx; int e = s+index->spread; if (e > multi->ngeoms) { e = multi->ngeoms; } for (int i = s; i < e; i++) { int index = multi->ixgeoms[i]; const struct tg_geom *child = multi->geoms[index]; if (tg_rect_intersects_rect(tg_geom_rect(child), rect)) { if (!iter(child, index, udata)) { return false; } } } } else { // branch const struct level *level = &index->levels[levelidx]; int s = startidx; int e = s+index->spread; if (e > level->nrects) { e = level->nrects; } for (int i = s; i < e; i++) { struct tg_rect brect; ixrect_to_tg_rect(&level->rects[i], &brect); if (tg_rect_intersects_rect(brect, rect)) { if (!multi_index_search(multi, rect, levelidx+1, i*index->spread, iter, udata)) { return false; } } } } return true; } /// Iterates over all child geometries in geom that intersect rect /// @note Only iterates over collection types: TG_MULTIPOINT, /// TG_MULTILINESTRING, TG_MULTIPOLYGON, and TG_GEOMETRYCOLLECTION. /// @note A GeoJSON FeatureCollection works as well. /// @see tg_geom_typeof() /// @see GeometryAccessors void tg_geom_search(const struct tg_geom *geom, struct tg_rect rect, bool (*iter)(const struct tg_geom *geom, int index, void *udata), void *udata) { const struct multi *multi = geom_multi(geom); if (!iter || !multi) return; if (!tg_rect_intersects_rect(tg_geom_rect(geom), rect)) { return; } if (!multi->index) { // sequential search for (int i = 0; i < multi->ngeoms; i++) { const struct tg_geom *child = multi->geoms[i]; if (tg_rect_intersects_rect(tg_geom_rect(child), rect)) { if (!iter(child, i, udata)) { return; } } } } else { // indexed search multi_index_search(multi, rect, 0, 0, iter, udata); } } /// Calculate the length of a line. double tg_line_length(const struct tg_line *line) { return tg_ring_perimeter((struct tg_ring*)line); } /// Parse data into a geometry by auto detecting the input type. /// The input data can be WKB, WKT, Hex, or GeoJSON. /// @param data Data /// @param len Length of data /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_ix() /// @see tg_geom_error() /// @see GeometryParsing struct tg_geom *tg_parse(const void *data, size_t len) { return tg_parse_ix(data, len, 0); } /// Parse data using provided indexing option. /// @param data Data /// @param len Length of data /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse() struct tg_geom *tg_parse_ix(const void *data, size_t len, enum tg_index ix) { if (!data || len == 0) { return 0; } const char *src = data; if (src[0] == '{') { goto geojson; } if (isspace(src[0])) { for (size_t i = 1; i < len; i++) { if (isspace(src[i])) { continue; } else if (src[i] == '{') { goto geojson; } else { break; } } goto wkt; } if (isalpha(src[0]) || isxdigit(src[0])) { bool ishex = true; for (size_t i = 0; i < len && i < 16; i++) { if (!isxdigit(src[i])) { ishex = false; break; } } if (ishex) { goto hex; } goto wkt; } if (src[0] == 0x00 || src[0] == 0x01) { goto wkb; } goto geobin; geojson: return tg_parse_geojsonn_ix(src, len, ix); wkt: return tg_parse_wktn_ix(src, len, ix); hex: return tg_parse_hexn_ix(src, len, ix); wkb: return tg_parse_wkb_ix((uint8_t*)src, len, ix); geobin: return tg_parse_geobin_ix((uint8_t*)src, len, ix); } /// Utility for returning an error message wrapped in a geometry. /// This operation does not return a real geometry, only an error message, /// which may be useful for generating custom errors from operations /// outside of the TG library. struct tg_geom *tg_geom_new_error(const char *error) { return error?make_parse_error("%s", error):0; } /// Set the noheap property to true and the reference counter to zero. /// _undocumented_ void tg_geom_setnoheap(struct tg_geom *geom) { geom->head.rc = 0; setnoheap(geom, 1); } /// Parse GeoBIN binary using provided indexing option. /// @param geobin GeoBIN data /// @param len Length of data /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_geobin_ix() /// @see tg_geom_error() /// @see tg_geom_geobin() /// @see https://github.com/tidwall/tg/blob/main/docs/GeoBIN.md /// @see GeometryParsing struct tg_geom *tg_parse_geobin(const uint8_t *geobin, size_t len) { return tg_parse_geobin_ix(geobin, len, 0); } static size_t parse_geobin(const uint8_t *geobin, size_t len, size_t i, size_t depth, enum tg_index ix, struct tg_geom **g) { if (i == len) goto invalid; if (depth > MAXDEPTH) goto invalid; int head = geobin[i]; if (head == 0x01) { return parse_wkb(geobin, len, i, depth, ix, g); } i++; if (head < 0x02 || head > 0x04) { goto invalid; } if (i == len) { goto invalid; } int dims = geobin[i++]; if (dims && (dims < 2 || dims > 4)) { goto invalid; } if (dims) { i += 8*dims*2; if (i >= len) { goto invalid; } } size_t xjsonlen = 0; const char *xjson = (const char*)(geobin+i); for (; i < len; i++) { if (geobin[i] == '\0') { i++; break; } xjsonlen++; } if (i == len) { goto invalid; } if (xjsonlen > 0 && !json_validn(xjson, xjsonlen)) { goto invalid; } struct tg_geom *geom; if (head == 0x04) { // FeatureCollection if (i+4 > len) { goto invalid; } uint32_t nfeats; memcpy(&nfeats, geobin+i, 4); i += 4; struct tg_geom **feats = tg_malloc(nfeats*sizeof(struct tg_geom*)); if (!feats) { return 0; } memset(feats, 0, nfeats*sizeof(struct tg_geom*)); struct tg_geom *feat = 0; uint32_t j = 0; for (; j < nfeats; j++) { i = parse_geobin(geobin, len, i, depth+1, ix, &feat); if (i == PARSE_FAIL) { break; } feats[j] = feat; } if (j == nfeats) { geom = tg_geom_new_geometrycollection((void*)feats, nfeats); } for (uint32_t k = 0; k < j; k++) { tg_geom_free(feats[k]); } tg_free(feats); if (j < nfeats) { *g = feat; // return the last failed feature return PARSE_FAIL; } if (!geom) { *g = 0; return PARSE_FAIL; } setflags(geom, IS_FEATURE_COL); } else { i = parse_wkb(geobin, len, i, depth, ix, &geom); } if (i == PARSE_FAIL || !geom) { *g = geom; return PARSE_FAIL; } if ((xjsonlen > 0 || head == 0x03) && getbase(geom) != BASE_GEOM) { // Wrap base in tg_geom struct tg_geom *g2 = geom_new(gettype(geom)); if (!g2) { tg_geom_free(geom); *g = 0; return PARSE_FAIL; } if (getbase(geom) == BASE_POINT) { g2->point = ((struct boxed_point*)geom)->point; boxed_point_free((struct boxed_point*)geom); } else { g2->line = (struct tg_line*)geom; } geom = g2; } if (head == 0x03) { setflags(geom, IS_FEATURE); } if (xjsonlen > 0) { geom->xjson = tg_malloc(xjsonlen+1); if (!geom->xjson) { tg_geom_free(geom); *g = 0; return PARSE_FAIL; } memcpy(geom->xjson, xjson, xjsonlen+1); } *g = geom; return i; invalid: *g = make_parse_error("invalid binary"); return PARSE_FAIL; } /// Parse GeoBIN binary using provided indexing option. /// @param geobin GeoBIN data /// @param len Length of data /// @param ix Indexing option, e.g. TG_NONE, TG_NATURAL, TG_YSTRIPES /// @returns A geometry or an error. Use tg_geom_error() after parsing to check /// for errors. /// @see tg_parse_geobin() struct tg_geom *tg_parse_geobin_ix(const uint8_t *geobin, size_t len, enum tg_index ix) { struct tg_geom *geom = NULL; parse_geobin(geobin, len, 0, 0, ix, &geom); if (!geom) return NULL; if ((getflags(geom)&IS_ERROR) == IS_ERROR) { struct tg_geom *gerr = make_parse_error("ParseError: %s", geom->error); tg_geom_free(geom); return gerr; } return geom; } static void write_geom_geobin(const struct tg_geom *geom, struct writer *wr); static void write_base_geom_geobin(const struct tg_geom *geom, struct writer *wr) { // extra json section const char *xjson = tg_geom_extra_json(geom); // write head byte if ((getflags(geom)&IS_FEATURE_COL) == IS_FEATURE_COL) { write_byte(wr, 0x04); } else if ((getflags(geom)&IS_FEATURE) == IS_FEATURE) { write_byte(wr, 0x03); } else if (gettype(geom) == TG_POINT && !xjson) { write_geom_point_wkb(geom, wr); return; } else { write_byte(wr, 0x02); } // mbr section double min[4], max[4]; int dims = tg_geom_fullrect(geom, min, max); write_byte(wr, dims); for (int i = 0; i < dims; i++) { write_doublele(wr, min[i]); } for (int i = 0; i < dims; i++) { write_doublele(wr, max[i]); } if (xjson) { write_string(wr, xjson); } write_byte(wr, 0); if ((getflags(geom)&IS_FEATURE_COL) == IS_FEATURE_COL) { // write feature collection int ngeoms = tg_geom_num_geometries(geom); write_uint32le(wr, (uint32_t)ngeoms); for (int i = 0; i < ngeoms; i++) { const struct tg_geom *g2 = tg_geom_geometry_at(geom, i); write_geom_geobin(g2, wr); } } else { // write wkb write_geom_wkb(geom, wr); } } static void write_point_geobin(struct boxed_point *point, struct writer *wr) { write_point_wkb(point, wr); } static void write_geobin_rect(struct writer *wr, struct tg_rect rect) { write_byte(wr, 2); // dims write_doublele(wr, rect.min.x); write_doublele(wr, rect.min.y); write_doublele(wr, rect.max.x); write_doublele(wr, rect.max.y); } static void write_line_geobin(struct tg_line *line, struct writer *wr) { write_byte(wr, 0x02); write_geobin_rect(wr, ((struct tg_ring*)line)->rect); write_byte(wr, 0); write_line_wkb(line, wr); } static void write_ring_geobin(struct tg_ring *ring, struct writer *wr) { write_byte(wr, 0x02); write_geobin_rect(wr, ring->rect); write_byte(wr, 0); write_ring_wkb(ring, wr); } static void write_poly_geobin(struct tg_poly *poly, struct writer *wr) { write_byte(wr, 0x02); write_geobin_rect(wr, poly->exterior->rect); write_byte(wr, 0); write_poly_wkb(poly, wr); } static void write_geom_geobin(const struct tg_geom *geom, struct writer *wr) { if ((getflags(geom)&IS_FEATURE) == IS_FEATURE) { goto base_geom; } switch (getbase(geom)) { case BASE_GEOM: base_geom: write_base_geom_geobin(geom, wr); break; case BASE_POINT: write_point_geobin((struct boxed_point*)geom, wr); break; case BASE_LINE: write_line_geobin((struct tg_line*)geom, wr); break; case BASE_RING: write_ring_geobin((struct tg_ring*)geom, wr); break; case BASE_POLY: write_poly_geobin((struct tg_poly*)geom, wr); break; } } /// Writes a GeoBIN representation of a geometry. /// /// The content is stored in the buffer pointed by dst. /// /// @param geom Input geometry /// @param dst Buffer where the resulting content is stored. /// @param n Maximum number of bytes to be used in the buffer. /// @return The number of characters needed to store the content into the /// buffer. /// If the returned length is greater than n, then only a partial copy /// occurred, for example: /// /// ``` /// uint8_t buf[64]; /// size_t len = tg_geom_geobin(geom, buf, sizeof(buf)); /// if (len > sizeof(buf)) { /// // ... write did not complete ... /// } /// ``` /// /// @see tg_geom_geojson() /// @see tg_geom_wkt() /// @see tg_geom_wkb() /// @see tg_geom_hex() /// @see GeometryWriting size_t tg_geom_geobin(const struct tg_geom *geom, uint8_t *dst, size_t n) { if (!geom) return 0; struct writer wr = { .dst = dst, .n = n }; write_geom_geobin(geom, &wr); return wr.count; } /// Returns the minimum bounding rectangle of a geometry on all dimensions. /// @param geom Input geometry /// @param min min values, must have room for 4 dimensions /// @param max max values, must have room for 4 dimensions /// @return number of dimensions, or zero if invalid geom. /// @see tg_geom_rect() int tg_geom_fullrect(const struct tg_geom *geom, double min[4], double max[4]) { if (!geom) { return 0; } struct tg_rect rect = tg_geom_rect(geom); min[0] = rect.min.x; min[1] = rect.min.y; min[2] = 0; min[3] = 0; max[0] = rect.max.x; max[1] = rect.max.y; max[2] = 0; max[3] = 0; int dims = 2; if (getbase(geom) == BASE_GEOM) { if (gettype(geom) == TG_POINT) { // Point if ((getflags(geom)&HAS_Z) == HAS_Z) { min[dims] = geom->z; max[dims] = geom->z; dims++; } if ((getflags(geom)&HAS_M) == HAS_M) { min[dims] = geom->m; max[dims] = geom->m; dims++; } } else if (gettype(geom) == TG_GEOMETRYCOLLECTION && geom->multi) { // GeometryCollection. Expand all child geometries struct tg_geom **geoms = geom->multi->geoms; int ngeoms = geom->multi->ngeoms; double gmin[4], gmax[4]; for (int i = 0; i < ngeoms; i++) { int gdims = tg_geom_fullrect(geoms[i], gmin, gmax); if (gdims >= 3) { if (dims == 2) { min[2] = gmin[2]; max[2] = gmax[2]; dims++; } else { min[2] = fmin0(min[2], gmin[2]); max[2] = fmax0(max[2], gmax[2]); } } if (gdims >= 4) { if (dims == 3) { min[3] = gmin[3]; max[3] = gmax[3]; dims++; } else { min[3] = fmin0(min[3], gmin[3]); max[3] = fmax0(max[3], gmax[3]); } } } } else { // Other geometries if ((getflags(geom)&HAS_Z) == HAS_Z) dims++; if ((getflags(geom)&HAS_M) == HAS_M) dims++; if (dims == 3 && geom->ncoords > 0) { min[2] = geom->coords[0]; max[2] = geom->coords[0]; for (int i = 1; i < geom->ncoords; i++) { min[2] = fmin0(min[2], geom->coords[i]); max[2] = fmax0(max[2], geom->coords[i]); } } else if (dims == 4 && geom->ncoords > 1) { min[2] = geom->coords[0]; min[3] = geom->coords[1]; max[2] = geom->coords[0]; max[3] = geom->coords[1]; for (int i = 2; i < geom->ncoords-1; i+=2) { min[2] = fmin0(min[2], geom->coords[i]); min[3] = fmin0(min[3], geom->coords[i+1]); max[2] = fmax0(max[2], geom->coords[i]); max[3] = fmax0(max[3], geom->coords[i+1]); } } } } return dims; } /// Returns the minimum bounding rectangle of GeoBIN data. /// @param geobin GeoBIN data /// @param len Length of data /// @param min min values, must have room for 4 dimensions /// @param max max values, must have room for 4 dimensions /// @return number of dimensions, or zero if rect cannot be determined. /// @see tg_geom_fullrect() /// @see tg_geom_rect() int tg_geobin_fullrect(const uint8_t *geobin, size_t len, double min[4], double max[4]) { size_t dims = 0; if (geobin && len > 2 && geobin[0] >= 0x01 && geobin[0] <= 0x04) { if (geobin[0] == 0x01 && len >= 5) { // Read Point uint32_t type; memcpy(&type, geobin+1, 4); switch (type) { case 1: dims = 2; break; case 1001: dims = 3; break; case 2001: dims = 3; break; case 3001: dims = 4; break; } if (dims > 0 && len >= 5+8*dims) { memcpy(min, geobin+5, 8*dims); memcpy(max, geobin+5, 8*dims); } } else if (geobin[0] != 0x01 && len >= 2+8*(size_t)geobin[1]*2){ // Read MBR dims = geobin[1]; memcpy(min, geobin+2, 8*dims); memcpy(max, geobin+2+8*dims, 8*dims); } } return dims; } /// Returns the minimum bounding rectangle of GeoBIN data. /// @param geobin GeoBIN data /// @param len Length of data /// @return the rectangle struct tg_rect tg_geobin_rect(const uint8_t *geobin, size_t len) { struct tg_rect rect = { 0 }; if (geobin && len > 2 && geobin[0] >= 0x01 && geobin[0] <= 0x04) { if (geobin[0] == 0x01 && len >= 21) { // Read Point uint32_t type; memcpy(&type, geobin+1, 4); if (type == 1 || type == 1001 || type == 2001 || type == 3001) { memcpy(&rect.min.x, geobin+5, 8); memcpy(&rect.min.y, geobin+5+8, 8); rect.max.x = rect.min.x; rect.max.y = rect.min.y; } } else if (geobin[0] != 0x01 && len >= 2+8*(size_t)geobin[1]*2){ // Read MBR int dims = geobin[1]; if (dims >= 2) { memcpy(&rect.min.x, geobin+2, 8); memcpy(&rect.min.y, geobin+2+8, 8); memcpy(&rect.max.x, geobin+2+8*dims, 8); memcpy(&rect.max.y, geobin+2+8*dims+8, 8); } } } return rect; } /// Returns the center point of GeoBIN data. /// @param geobin GeoBIN data /// @param len Length of data /// @return the center point struct tg_point tg_geobin_point(const uint8_t *geobin, size_t len) { struct tg_point point = { 0 }; if (geobin && len > 2 && geobin[0] >= 0x01 && geobin[0] <= 0x04) { if (geobin[0] == 0x01 && len >= 21) { // Read Point uint32_t type; memcpy(&type, geobin+1, 4); if (type == 1 || type == 1001 || type == 2001 || type == 3001) { memcpy(&point.x, geobin+5, 8); memcpy(&point.y, geobin+5+8, 8); } } else if (geobin[0] != 0x01 && len >= 2+8*(size_t)geobin[1]*2){ // Read MBR struct tg_rect rect = { 0 }; int dims = geobin[1]; if (dims >= 2) { memcpy(&rect.min.x, geobin+2, 8); memcpy(&rect.min.y, geobin+2+8, 8); memcpy(&rect.max.x, geobin+2+8*dims, 8); memcpy(&rect.max.y, geobin+2+8*dims+8, 8); } point = tg_rect_center(rect); } } return point; }