Patch support, more unit tests, hexwkb,

wkb, string output.
This commit is contained in:
Paul Ramsey 2013-01-29 15:34:48 -08:00
parent ef1e0435f9
commit fe5bf854b6
21 changed files with 1100 additions and 297 deletions

View File

@ -34,19 +34,39 @@ Human-Readable Text
( <dim1>, <dim2>, <dim3>, <dim4> ),
( <dim1>, <dim2>, <dim3>, <dim4> ) ]
Uncompressed Binary
-------------------
Point Binary
------------
PCPOINT
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uchar[]: pointdata (interpret relative to pcid)
Patch Binary
------------
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: compression (0 = no compression, 1 = dimensional, 2 = GHT)
uchar[]: data (interpret relative to pcid and compression)
Patch Binary (Uncompressed)
---------------------------
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: 0 = no compression
uint32: npoints
pointdata[]: interpret relative to pcid
Patch Binary (Dimensional)
--------------------------
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: 1 = dimensional compression
dimensional[]: dimensionally compressed data for each dimension
/* dimensional compression */
byte: dimensional compression type (0 = none, 1 = significant bits, 2 = deflate, 3 = run-length)
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uchar[]: data (interpret relative to pcid)
PCPATCH
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: npoints
uchar[]: data (interpret relative to pcid)
/* TO DO, more on dimensional compression contents */

View File

@ -8,6 +8,7 @@ OBJS = \
pc_mem.o \
pc_patch.o \
pc_point.o \
pc_pointlist.o \
pc_schema.o \
pc_util.o \
pc_val.o \

View File

@ -12,6 +12,7 @@ EXE = cu_tester
OBJS = \
cu_tester.o \
cu_pc_schema.o \
cu_pc_point.o \
cu_pc_patch.o
# Build the unit tester

View File

@ -13,7 +13,9 @@
/* GLOBALS ************************************************************/
static PCSCHEMA *schema = NULL;
const char *xmlfile = "data/pdal-schema.xml";
static PCSCHEMA *simpleschema = NULL;
static const char *xmlfile = "data/pdal-schema.xml";
static const char *simplexmlfile = "data/simple-schema.xml";
/* Setup/teardown for this suite */
static int
@ -23,6 +25,12 @@ init_suite(void)
int rv = pc_schema_from_xml(xmlstr, &schema);
pcfree(xmlstr);
if ( rv == PC_FAILURE ) return 1;
xmlstr = file_to_str(simplexmlfile);
rv = pc_schema_from_xml(xmlstr, &simpleschema);
pcfree(xmlstr);
if ( rv == PC_FAILURE ) return 1;
return 0;
}
@ -30,6 +38,7 @@ static int
clean_suite(void)
{
pc_schema_free(schema);
pc_schema_free(simpleschema);
return 0;
}
@ -63,9 +72,9 @@ test_endian_flip()
CU_ASSERT_DOUBLE_EQUAL(a3, b3, 0.0000001);
CU_ASSERT_DOUBLE_EQUAL(a4, b4, 0.0000001);
ptr = bytes_flip_endian(pt->data, schema, 1);
ptr = uncompressed_bytes_flip_endian(pt->data, schema, 1);
pcfree(pt->data);
pt->data = bytes_flip_endian(ptr, schema, 1);
pt->data = uncompressed_bytes_flip_endian(ptr, schema, 1);
b1 = pc_point_get_double_by_name(pt, "X");
b2 = pc_point_get_double_by_name(pt, "Z");
@ -77,7 +86,36 @@ test_endian_flip()
CU_ASSERT_DOUBLE_EQUAL(a4, b4, 0.0000001);
}
static void
test_patch_hex_inout()
{
// 00 endian
// 00000000 pcid
// 00000000 compression
// 00000002 npoints
// 0000000200000003000000050006 pt1 (XYZi)
// 0000000200000003000000050008 pt2 (XYZi)
char *hexbuf = "0000000000000000000000000200000002000000030000000500060000000200000003000000050008";
double d;
char *str;
size_t hexsize = strlen(hexbuf);
uint8_t *wkb = bytes_from_hexbytes(hexbuf, hexsize);
PCPATCH *pa = pc_patch_from_wkb(simpleschema, wkb, hexsize/2);
PCPOINTLIST *pl = pc_patch_to_points(pa);
d = pc_point_get_double_by_name(pl->points[0], "X");
CU_ASSERT_DOUBLE_EQUAL(d, 0.02, 0.000001);
d = pc_point_get_double_by_name(pl->points[1], "Intensity");
CU_ASSERT_DOUBLE_EQUAL(d, 8, 0.000001);
str = pc_patch_to_string(pa);
CU_ASSERT_STRING_EQUAL(str, "[ 0 : (0.02, 0.03, 0.05, 6), (0.02, 0.03, 0.05, 8) ]");
// printf("\n%s\n",str);
pc_pointlist_free(pl);
pc_patch_free(pa);
pcfree(wkb);
}
@ -85,6 +123,7 @@ test_endian_flip()
CU_TestInfo patch_tests[] = {
PG_TEST(test_endian_flip),
PG_TEST(test_patch_hex_inout),
CU_TEST_INFO_NULL
};

93
libpc/cunit/cu_pc_point.c Normal file
View File

@ -0,0 +1,93 @@
/***********************************************************************
* cu_pc_schema.c
*
* Testing for the schema API functions
*
* Portions Copyright (c) 2012, OpenGeo
*
***********************************************************************/
#include "CUnit/Basic.h"
#include "cu_tester.h"
/* GLOBALS ************************************************************/
static PCSCHEMA *schema = NULL;
static const char *xmlfile = "data/simple-schema.xml";
// SIMPLE SCHEMA
// int32_t x
// int32_t y
// int32_t z
// int16_t intensity
/* Setup/teardown for this suite */
static int
init_suite(void)
{
char *xmlstr = file_to_str(xmlfile);
int rv = pc_schema_from_xml(xmlstr, &schema);
pcfree(xmlstr);
if ( rv == PC_FAILURE ) return 1;
return 0;
}
static int
clean_suite(void)
{
pc_schema_free(schema);
return 0;
}
/* TESTS **************************************************************/
static void
test_point_hex_inout()
{
// byte: endianness (1 = NDR, 0 = XDR)
// uint32: pcid (key to POINTCLOUD_SCHEMAS)
// uchar[]: pointdata (interpret relative to pcid)
double d;
char *hexbuf = "00000000010000000100000002000000030004";
size_t hexsize = strlen(hexbuf);
uint8_t *wkb = bytes_from_hexbytes(hexbuf, hexsize);
PCPOINT *pt = pc_point_from_wkb(schema, wkb, hexsize/2);
d = pc_point_get_double_by_name(pt, "X");
CU_ASSERT_DOUBLE_EQUAL(d, 0.01, 0.000001);
d = pc_point_get_double_by_name(pt, "Y");
CU_ASSERT_DOUBLE_EQUAL(d, 0.02, 0.000001);
d = pc_point_get_double_by_name(pt, "Z");
CU_ASSERT_DOUBLE_EQUAL(d, 0.03, 0.000001);
d = pc_point_get_double_by_name(pt, "Intensity");
CU_ASSERT_DOUBLE_EQUAL(d, 4, 0.0001);
pc_point_free(pt);
pcfree(wkb);
hexbuf = "01010000000100000002000000030000000500";
hexsize = strlen(hexbuf);
wkb = bytes_from_hexbytes(hexbuf, hexsize);
pt = pc_point_from_wkb(schema, wkb, hexsize/2);
d = pc_point_get_double_by_name(pt, "X");
CU_ASSERT_DOUBLE_EQUAL(d, 0.01, 0.000001);
d = pc_point_get_double_by_name(pt, "Y");
CU_ASSERT_DOUBLE_EQUAL(d, 0.02, 0.000001);
d = pc_point_get_double_by_name(pt, "Z");
CU_ASSERT_DOUBLE_EQUAL(d, 0.03, 0.000001);
d = pc_point_get_double_by_name(pt, "Intensity");
CU_ASSERT_DOUBLE_EQUAL(d, 5, 0.0001);
pc_point_free(pt);
pcfree(wkb);
}
/* REGISTER ***********************************************************/
CU_TestInfo point_tests[] = {
PG_TEST(test_point_hex_inout),
CU_TEST_INFO_NULL
};
CU_SuiteInfo point_suite = {"point", init_suite, clean_suite, point_tests};

View File

@ -15,6 +15,7 @@
/* ADD YOUR SUITE HERE (1 of 2) */
extern CU_SuiteInfo schema_suite;
extern CU_SuiteInfo patch_suite;
extern CU_SuiteInfo point_suite;
/*
** The main() function for setting up and running the tests.
@ -28,6 +29,7 @@ int main(int argc, char *argv[])
{
schema_suite,
patch_suite,
point_suite,
CU_SUITE_INFO_NULL
};

View File

@ -8,8 +8,6 @@
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>2ee118d1-119e-4906-99c3-42934203f872</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
@ -19,8 +17,6 @@
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>87707eee-2f30-4979-9987-8ef747e30275</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>3</pc:position>
@ -30,8 +26,6 @@
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>e74b5e41-95e6-4cf2-86ad-e3f5a996da5d</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>4</pc:position>
@ -41,8 +35,6 @@
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>61e90c9a-42fc-46c7-acd3-20d67bd5626f</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>5</pc:position>
@ -52,8 +44,6 @@
<pc:interpretation>uint8_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>ffe5e5f8-4cec-4560-abf0-448008f7b89e</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>6</pc:position>
@ -63,8 +53,6 @@
<pc:interpretation>uint8_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>7c28bfd4-a9ed-4fb2-b07f-931c076fbaf0</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>7</pc:position>
@ -74,8 +62,6 @@
<pc:interpretation>uint8_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>13019a2c-cf88-480d-a995-0162055fe5f9</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>8</pc:position>
@ -85,8 +71,6 @@
<pc:interpretation>uint8_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>108c18f2-5cc0-4669-ae9a-f41eb4006ea5</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>9</pc:position>
@ -96,8 +80,6 @@
<pc:interpretation>uint8_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>b4c67de9-cef1-432c-8909-7c751b2a4e0b</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>10</pc:position>
@ -107,8 +89,6 @@
<pc:interpretation>int8_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>aaadaf77-e0c9-4df0-81a7-27060794cd69</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>11</pc:position>
@ -118,8 +98,6 @@
<pc:interpretation>uint8_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>70eb558e-63d4-4804-b1db-fc2fd716927c</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>12</pc:position>
@ -129,8 +107,6 @@
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
<pc:active>true</pc:active>
<pc:uuid>4e42e96a-6af0-4fdd-81cb-6216ff47bf6b</pc:uuid>
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
</pc:dimension>
<pc:dimension>
<pc:position>13</pc:position>

View File

@ -17,7 +17,7 @@
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:position>3</pc:position>
<pc:size>4</pc:size>
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Z</pc:name>

View File

@ -30,7 +30,8 @@
enum COMPRESSIONS
{
PC_NONE = 0,
PC_GHT = 1
PC_GHT = 1,
PC_DIMENSIONAL = 2
};
/**
@ -87,9 +88,17 @@ typedef struct
{
int8_t readonly;
const PCSCHEMA *schema;
uint8_t *data;
uint8_t *data; /* A serialized version of the data */
} PCPOINT;
typedef struct
{
int8_t readonly;
uint32_t npoints;
uint32_t maxpoints;
PCPOINT **points;
} PCPOINTLIST;
/**
* Uncompressed Structure for in-memory handling
* of patches. A read-only PgSQL patch can be wrapped in
@ -100,11 +109,13 @@ typedef struct
typedef struct
{
int8_t readonly;
size_t npoints; /* How many points we have */
size_t maxpoints; /* How man points we can hold (or 0 for read-only) */
uint32_t npoints; /* How many points we have */
uint32_t maxpoints; /* How man points we can hold (or 0 for read-only) */
const PCSCHEMA *schema;
double xmin, xmax, ymin, ymax;
uint8_t *data;
uint8_t compressed; /* Has compression been applied to the data buffer? */
size_t datasize;
uint8_t *data; /* A serialized version of the data */
} PCPATCH;
@ -146,16 +157,16 @@ void pc_install_default_handlers(void);
* UTILITY
*/
/** Read a hex string into binary buffer */
/** Read the the PCID from WKB form of a POINT/PATCH */
uint32_t wkb_get_pcid(uint8_t *wkb);
/** Read the the npoints from WKB form of a PATCH */
uint32_t wkb_get_compression(uint8_t *wkb);
/** Flips the bytes of an int32_t */
int32_t int32_flip_endian(int32_t val);
/** Convert binary to hex */
uint8_t* bytes_from_hexbytes(const char *hexbuf, size_t hexsize);
/** Read the the PCID from WKB form of a POINT */
uint32_t wkb_get_pcid(uint8_t *bytes);
/** Get a pointer to the data buffer of the WKB form of a point */
uint8_t* wkb_point_get_data_ptr(uint8_t *bytes);
/** Force a byte array into the machine endianness */
uint8_t* bytes_flip_endian(const uint8_t *bytebuf, const PCSCHEMA *schema, uint32_t npoints);
/** Convert hex to binary */
char* hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize);
/**********************************************************************
@ -176,6 +187,19 @@ PCDIMENSION* pc_schema_get_dimension_by_name(const PCSCHEMA *s, const char *name
uint32_t pc_schema_is_valid(const PCSCHEMA *s);
/**********************************************************************
* PCPOINTLIST
*/
/** Allocate a pointlist */
PCPOINTLIST* pc_pointlist_make(uint32_t npoints);
/** Free a pointlist, including the points contained therein */
void pc_pointlist_free(PCPOINTLIST *pl);
/** Add a point to the list, expanding buffer as necessary */
void pc_pointlist_add_point(PCPOINTLIST *pl, PCPOINT *pt);
/**********************************************************************
* PCPOINT
@ -183,27 +207,42 @@ uint32_t pc_schema_is_valid(const PCSCHEMA *s);
/** Create a new PCPOINT */
PCPOINT* pc_point_make(const PCSCHEMA *s);
/** Create a new readonly PCPOINT on top of a data buffer */
PCPOINT* pc_point_from_data(const PCSCHEMA *s, const uint8_t *data);
/** Create a new read/write PCPOINT on top of a data buffer */
PCPOINT* pc_point_from_data_rw(const PCSCHEMA *s, uint8_t *data);
/** Create a new read/write PCPOINT from a double array */
PCPOINT* pc_point_from_double_array(const PCSCHEMA *s, double *array, uint32_t nelems);
/** Frees the PTPOINT and data (if not readonly) does not free referenced schema */
/** Frees the PTPOINT and data (if not readonly). Does not free referenced schema */
void pc_point_free(PCPOINT *pt);
/** Casts named dimension value to double and scale/offset appropriately before returning */
double pc_point_get_double_by_name(const PCPOINT *pt, const char *name);
/** Casts dimension value to double and scale/offset appropriately before returning */
double pc_point_get_double_by_index(const PCPOINT *pt, uint32_t idx);
/** Scales/offsets double, casts to appropriate dimension type, and writes into point */
int pc_point_set_double_by_index(PCPOINT *pt, uint32_t idx, double val);
/** Scales/offsets double, casts to appropriate dimension type, and writes into point */
int pc_point_set_double_by_name(PCPOINT *pt, const char *name, double val);
/** Returns X coordinate */
double pc_point_get_x(const PCPOINT *pt);
/** Returns Y coordinate */
double pc_point_get_y(const PCPOINT *pt);
/** Create a new readwrite PCPOINT from a hex byte array */
PCPOINT* pc_point_from_wkb(const PCSCHEMA *s, uint8_t *wkb, size_t wkbsize);
/** Returns serialized form of point */
uint8_t* pc_point_to_wkb(const PCPOINT *pt, size_t *wkbsize);
/** Returns text form of point */
char* pc_point_to_string(const PCPOINT *pt);
/**********************************************************************
@ -212,11 +251,34 @@ double pc_point_get_y(const PCPOINT *pt);
/** Create new empty PCPATCH */
PCPATCH* pc_patch_make(const PCSCHEMA *s);
/** Create new PCPATCH from a PCPOINT set. Copies data, doesn't take ownership of points */
PCPATCH* pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts);
PCPATCH* pc_patch_from_points(const PCPOINT **pts, uint32_t numpts);
/** Returns a list of points extracted from patch */
PCPOINTLIST* pc_patch_to_points(const PCPATCH *patch);
/** Free patch memory, respecting read-only status. Does not free referenced schema */
void pc_patch_free(PCPATCH *patch);
/** Add a point to read/write PCPATCH */
int pc_patch_add_point(PCPATCH *c, const PCPOINT *p);
/** Create a compressed copy, using the compression schema referenced in the PCSCHEMA */
PCPATCH* pc_patch_compress(const PCPATCH *patch);
/** Create a full copy of the patch, including data, but not including PCSCHEMA */
PCPATCH* pc_patch_clone(const PCPATCH *patch);
/** Create a new readwrite PCPOINT from a byte array */
PCPATCH* pc_patch_from_wkb(const PCSCHEMA *s, uint8_t *wkb, size_t wkbsize);
/** Returns serialized form of point */
uint8_t* pc_patch_to_wkb(const PCPATCH *patch, size_t *wkbsize);
/** Returns text form of patch */
char* pc_patch_to_string(const PCPATCH *patch);
#endif /* _PC_API_H */

View File

@ -63,6 +63,12 @@ static size_t INTERPRETATION_SIZES[NUM_INTERPRETATIONS] =
8, 4 /* PC_DOUBLE, PC_FLOAT */
};
/** What is the endianness of this system? */
char machine_endian(void);
/** Force a byte array into the machine endianness */
uint8_t* uncompressed_bytes_flip_endian(const uint8_t *bytebuf, const PCSCHEMA *schema, uint32_t npoints);
/** Read interpretation type from buffer and cast to double */
double pc_double_from_ptr(const uint8_t *ptr, uint32_t interpretation);

View File

@ -10,12 +10,14 @@
#include <math.h>
#include "pc_api_internal.h"
#include "stringbuffer.h"
PCPATCH *
pc_patch_make(const PCSCHEMA *s)
{
PCPATCH *pch;
uint32_t maxpoints = PCPATCH_DEFAULT_MAXPOINTS;
size_t datasize;
if ( ! s )
{
@ -32,7 +34,10 @@ pc_patch_make(const PCSCHEMA *s)
/* Make our own data area */
pch = pcalloc(sizeof(PCPATCH));
pch->data = pcalloc(s->size * maxpoints);
pch->compressed = PC_FALSE;
datasize = s->size * maxpoints;
pch->data = pcalloc(datasize);
pch->datasize = datasize;
/* Initialize bounds */
pch->xmin = pch->ymin = MAXFLOAT;
@ -46,12 +51,58 @@ pc_patch_make(const PCSCHEMA *s)
return pch;
}
static int
pc_patch_compute_extent_uncompressed(PCPATCH *patch)
{
int i;
PCPOINT *pt = pc_point_from_data(patch->schema, patch->data);
/* Initialize bounds */
patch->xmin = patch->ymin = MAXFLOAT;
patch->xmax = patch->ymax = -1 * MAXFLOAT;
/* Calculate bounds */
for ( i = 0; i < patch->npoints; i++ )
{
double x, y;
/* Just push the data buffer forward by one point at a time */
pt->data = patch->data + i * patch->schema->size;
x = pc_point_get_x(pt);
y = pc_point_get_y(pt);
if ( patch->xmin > x ) patch->xmin = x;
if ( patch->ymin > y ) patch->ymin = y;
if ( patch->xmax < x ) patch->xmax = x;
if ( patch->ymax < y ) patch->ymax = y;
}
return PC_SUCCESS;
}
static int
pc_patch_compute_extent(PCPATCH *patch)
{
switch( patch->schema->compression )
{
case PC_NONE:
return pc_patch_compute_extent_uncompressed(patch);
case PC_GHT:
return PC_FAILURE;
case PC_DIMENSIONAL:
return PC_FAILURE;
}
return PC_FAILURE;
}
void
pc_patch_free(PCPATCH *patch)
{
if ( ! patch->readonly )
{
pcfree(patch->data);
}
pcfree(patch);
}
int
pc_patch_add_point(PCPATCH *c, const PCPOINT *p)
{
@ -76,6 +127,12 @@ pc_patch_add_point(PCPATCH *c, const PCPOINT *p)
pcerror("pc_patch_add_point: cannot add point to readonly patch");
return PC_FAILURE;
}
if ( c->compressed && c->schema->compression != PC_NONE )
{
pcerror("pc_patch_add_point: cannot add point to compressed patch");
return PC_FAILURE;
}
sz = c->schema->size;
@ -83,7 +140,8 @@ pc_patch_add_point(PCPATCH *c, const PCPOINT *p)
if ( c->npoints == c->maxpoints )
{
c->maxpoints *= 2;
c->data = pcrealloc(c->data, c->maxpoints * sz);
c->datasize = c->maxpoints * sz;
c->data = pcrealloc(c->data, c->datasize);
}
/* Copy the data buffer from point to patch */
@ -102,8 +160,10 @@ pc_patch_add_point(PCPATCH *c, const PCPOINT *p)
return PC_SUCCESS;
}
PCPATCH *
pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
pc_patch_from_points(const PCPOINT **pts, uint32_t numpts)
{
PCPATCH *pch;
const PCSCHEMA *s;
@ -112,13 +172,13 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
if ( ! numpts )
{
pcerror("zero point count passed into pc_patch_make_from_points");
pcerror("zero point count passed into pc_patch_from_points");
return NULL;
}
if ( ! pts )
{
pcerror("null point array passed into pc_patch_make_from_points");
pcerror("null point array passed into pc_patch_from_points");
return NULL;
}
@ -129,7 +189,7 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
/* Confirm width of a point data buffer */
if ( ! s->size )
{
pcerror("invalid point size in pc_patch_make_from_points");
pcerror("invalid point size in pc_patch_from_points");
return NULL;
}
@ -144,6 +204,7 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
/* Set up basic info */
pch->readonly = PC_FALSE;
pch->compressed = PC_FALSE;
pch->maxpoints = numpts;
pch->schema = s;
pch->npoints = 0;
@ -154,7 +215,7 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
{
if ( pts[i]->schema != s )
{
pcerror("points do not share a schema in pc_patch_make_from_points");
pcerror("points do not share a schema in pc_patch_from_points");
return NULL;
}
memcpy(ptr, pts[i]->data, s->size);
@ -163,10 +224,279 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
}
else
{
pcwarn("encountered null point in pc_patch_make_from_points");
pcwarn("encountered null point in pc_patch_from_points");
}
}
return pch;
}
PCPOINTLIST *
pc_patch_to_points_uncompressed(const PCPATCH *patch)
{
int i;
PCPOINTLIST *pl;
size_t pt_size = patch->schema->size;
uint32_t npoints = patch->npoints;
pl = pc_pointlist_make(npoints);
for ( i = 0; i < npoints; i++ )
{
pc_pointlist_add_point(pl, pc_point_from_data(patch->schema, patch->data + i*pt_size));
}
return pl;
}
PCPOINTLIST *
pc_patch_to_points(const PCPATCH *patch)
{
uint32_t compression = patch->schema->compression;
if ( ! patch->compressed )
return pc_patch_to_points_uncompressed(patch);
switch ( compression )
{
case PC_NONE:
{
return pc_patch_to_points_uncompressed(patch);
}
case PC_GHT:
{
// return pc_patch_to_points_ght(patch);
}
case PC_DIMENSIONAL:
{
// return pc_patch_to_points_dimensional(patch);
}
}
/* Don't get here */
pcerror("pc_patch_to_points: unsupported compression type %d", compression);
return NULL;
}
static PCPATCH *
pc_patch_compress_dimensional(const PCPATCH *patch)
{
pcerror("pc_patch_compress_dimensional unimplemented");
return NULL;
}
static PCPATCH *
pc_patch_compress_ght(const PCPATCH *patch)
{
pcerror("pc_patch_compress_ght unimplemented");
return NULL;
}
PCPATCH *
pc_patch_compress(const PCPATCH *patch)
{
uint32_t compression = patch->schema->compression;
if ( patch->compressed )
return pc_patch_clone(patch);
switch ( compression )
{
case PC_NONE:
{
PCPATCH *newpatch = pc_patch_clone(patch);
newpatch->compressed = PC_TRUE;
return newpatch;
}
case PC_GHT:
{
return pc_patch_compress_ght(patch);
}
case PC_DIMENSIONAL:
{
return pc_patch_compress_dimensional(patch);
}
}
/* Don't get here */
pcerror("pc_patch_compress: unknown compression type %d", compression);
return NULL;
}
PCPATCH *
pc_patch_clone(const PCPATCH *patch)
{
PCPATCH *newpatch = pcalloc(sizeof(PCPATCH));
memcpy(newpatch, patch, sizeof(PCPATCH));
newpatch->data = pcalloc(newpatch->datasize);
memcpy(newpatch->data, patch->data, newpatch->datasize);
return newpatch;
}
static PCPATCH *
pc_patch_from_wkb_uncompressed(const PCSCHEMA *s, uint8_t *wkb, size_t wkbsize)
{
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: compression (0 = no compression, 1 = dimensional, 2 = GHT)
uint32: npoints
pcpoint[]: data (interpret relative to pcid)
*/
static size_t hdrsz = 1+4+4+4; /* endian + pcid + compression + npoints */
PCPATCH *patch;
uint8_t *data;
uint8_t swap_endian = (wkb[0] != machine_endian());
uint32_t npoints;
if ( wkb_get_compression(wkb) != PC_NONE )
{
pcerror("pc_patch_from_wkb_uncompressed: call with wkb that is not uncompressed");
return NULL;
}
npoints = wkb_get_npoints(wkb);
if ( (wkbsize - hdrsz) != (s->size * npoints) )
{
pcerror("pc_patch_from_wkb_uncompressed: wkb size and expected data size do not match");
return NULL;
}
if ( swap_endian )
{
data = uncompressed_bytes_flip_endian(wkb+hdrsz, s, npoints);
}
else
{
data = pcalloc(npoints * s->size);
memcpy(data, wkb+hdrsz, npoints*s->size);
}
patch = pcalloc(sizeof(PCPATCH));
patch->npoints = npoints;
patch->maxpoints = npoints;
patch->schema = s;
patch->compressed = PC_TRUE; /* It's in whatever compression it came in */
patch->datasize = (wkbsize - hdrsz);
patch->data = data;
if ( PC_FAILURE == pc_patch_compute_extent(patch) )
{
pcerror("pc_patch_compute_extent failed");
}
return patch;
}
PCPATCH *
pc_patch_from_wkb(const PCSCHEMA *s, uint8_t *wkb, size_t wkbsize)
{
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: compression (0 = no compression, 1 = dimensional, 2 = GHT)
uchar[]: data (interpret relative to pcid and compression)
*/
uint32_t compression, pcid;
if ( ! wkbsize )
{
pcerror("pc_patch_from_wkb: zero length wkb");
}
pcid = wkb_get_pcid(wkb);
compression = wkb_get_compression(wkb);
if ( compression != s->compression )
{
pcerror("pc_patch_from_wkb: wkb compression (%d) not consistent with schema compression (%d)", compression, s->compression);
}
if ( pcid != s->pcid )
{
pcerror("pc_patch_from_wkb: wkb pcid (%d) not consistent with schema pcid (%d)", pcid, s->pcid);
}
switch ( compression )
{
case PC_NONE:
{
return pc_patch_from_wkb_uncompressed(s, wkb, wkbsize);
}
case PC_GHT:
{
pcerror("pc_patch_from_wkb: GHT compression not yet supported");
return NULL;
}
case PC_DIMENSIONAL:
{
pcerror("pc_patch_from_wkb: Dimensional compression not yet supported");
return NULL;
}
}
/* Don't get here */
pcerror("pc_patch_from_wkb: unknown compression '%d' requested", compression);
return NULL;
}
uint8_t *
pc_patch_to_wkb(const PCPATCH *patch, size_t *wkbsize)
{
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: compression (0 = no compression, 1 = dimensional, 2 = GHT)
uchar[]: data (interpret relative to pcid and compression)
*/
char endian = machine_endian();
size_t size = 1 + 4 + 4 + patch->datasize;
uint8_t *wkb = pcalloc(size);
wkb[0] = endian; /* Write endian flag */
memcpy(wkb + 1, &(patch->schema->pcid), 4); /* Write PCID */
memcpy(wkb + 5, patch->data, patch->datasize); /* Write data */
if ( wkbsize ) *wkbsize = size;
return wkb;
}
char *
pc_patch_to_string(const PCPATCH *patch)
{
/* ( <pcid> : (<dim1>, <dim2>, <dim3>, <dim4>), (<dim1>, <dim2>, <dim3>, <dim4>))*/
stringbuffer_t *sb = stringbuffer_create();
PCPOINTLIST *pl;
char *str;
int i, j;
pl = pc_patch_to_points(patch);
stringbuffer_aprintf(sb, "[ %d : ", patch->schema->pcid);
for ( i = 0; i < pl->npoints; i++ )
{
PCPOINT *pt = pl->points[i];
if ( i )
{
stringbuffer_append(sb, ", ");
}
stringbuffer_append(sb, "(");
for ( j = 0; j < pt->schema->ndims; j++ )
{
if ( j )
{
stringbuffer_append(sb, ", ");
}
stringbuffer_aprintf(sb, "%g", pc_point_get_double_by_index(pt, j));
}
stringbuffer_append(sb, ")");
}
stringbuffer_append(sb, " ]");
/* All done, copy and clean up */
pc_pointlist_free(pl);
str = stringbuffer_getstringcopy(sb);
stringbuffer_destroy(sb);
return str;
}

View File

@ -64,14 +64,6 @@ pc_point_from_data(const PCSCHEMA *s, const uint8_t *data)
return pt;
}
PCPOINT *
pc_point_from_data_rw(const PCSCHEMA *s, uint8_t *data)
{
PCPOINT *pt = pc_point_from_data(s, data);
pt->readonly = PC_FALSE;
return pt;
}
void
pc_point_free(PCPOINT *pt)
@ -168,7 +160,7 @@ pc_point_get_y(const PCPOINT *pt)
}
char *
pc_text_from_point(const PCPOINT *pt, size_t *sersize)
pc_point_to_string(const PCPOINT *pt)
{
/* ( <pcid> : <dim1>, <dim2>, <dim3>, <dim4> )*/
stringbuffer_t *sb = stringbuffer_create();
@ -223,6 +215,65 @@ pc_point_from_double_array(const PCSCHEMA *s, double *array, uint32_t nelems)
}
return pt;
}
}
PCPOINT *
pc_point_from_wkb(const PCSCHEMA *schema, uint8_t *wkb, size_t wkblen)
{
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uchar[]: data (interpret relative to pcid)
*/
const size_t hdrsz = 1+4; /* endian + pcid */
uint8_t wkb_endian;
uint32_t pcid;
uint8_t *data;
PCPOINT *pt;
if ( ! wkblen )
{
pcerror("pc_point_from_wkb: zero length wkb");
}
wkb_endian = wkb[0];
pcid = wkb_get_pcid(wkb);
if ( (wkblen-hdrsz) != schema->size )
{
pcerror("pc_point_from_wkb: wkb size inconsistent with schema size");
}
if ( wkb_endian != machine_endian() )
{
/* uncompressed_bytes_flip_endian creates a flipped copy */
data = uncompressed_bytes_flip_endian(wkb+hdrsz, schema, 1);
}
else
{
data = pcalloc(schema->size);
memcpy(data, wkb+hdrsz, wkblen-hdrsz);
}
pt = pc_point_from_data(schema, data);
pt->readonly = PC_FALSE;
return pt;
}
uint8_t *
pc_point_to_wkb(const PCPOINT *pt, size_t *wkbsize)
{
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uchar[]: data (interpret relative to pcid)
*/
char endian = machine_endian();
size_t size = 1 + 4 + pt->schema->size;
uint8_t *wkb = pcalloc(size);
wkb[0] = endian; /* Write endian flag */
memcpy(wkb + 1, &(pt->schema->pcid), 4); /* Write PCID */
memcpy(wkb + 5, pt->data, pt->schema->size); /* Write data */
if ( wkbsize ) *wkbsize = size;
return wkb;
}

50
libpc/pc_pointlist.c Normal file
View File

@ -0,0 +1,50 @@
/***********************************************************************
* pc_pointlist.c
*
* Point list handling. Create, get and set values from the
* basic PCPOINTLIST structure.
*
* Portions Copyright (c) 2012, OpenGeo
*
***********************************************************************/
#include "pc_api_internal.h"
PCPOINTLIST *
pc_pointlist_make(uint32_t npoints)
{
PCPOINTLIST *pl = pcalloc(sizeof(PCPOINTLIST));
pl->points = pcalloc(sizeof(PCPOINT*) * npoints);
pl->maxpoints = npoints;
pl->npoints = 0;
pl->readonly = PC_FALSE;
return pl;
}
void
pc_pointlist_free(PCPOINTLIST *pl)
{
int i;
for ( i = 0; i < pl->npoints; i++ )
{
pc_point_free(pl->points[i]);
}
pcfree(pl->points);
pcfree(pl);
return;
}
void
pc_pointlist_add_point(PCPOINTLIST *pl, PCPOINT *pt)
{
if ( pl->npoints >= pl->maxpoints )
{
if ( pl->maxpoints < 1 ) pl->maxpoints = 1;
pl->maxpoints *= 2;
pl->points = pcrealloc(pl->points, pl->maxpoints * sizeof(PCPOINT*));
}
pl->points[pl->npoints] = pt;
pl->npoints += 1;
return;
}

View File

@ -311,6 +311,10 @@ pc_schema_from_xml(const char *xml_str, PCSCHEMA **schema)
d->interpretation = pc_interpretation_number(child->children->content);
else if ( strcmp(child->name, "scale") == 0 )
d->scale = atof(child->children->content);
else if ( strcmp(child->name, "uuid") == 0 )
/* Ignore this tag for now */ 1;
else if ( strcmp(child->name, "parent_uuid") == 0 )
/* Ignore this tag for now */ 1;
else
pcinfo("unhandled schema type element \"%s\" encountered", child->name);
}

View File

@ -9,8 +9,180 @@
#include "pc_api_internal.h"
/**********************************************************************************
* WKB AND ENDIANESS UTILITIES
*/
/* Our static character->number map. Anything > 15 is invalid */
static uint8_t hex2char[256] = {
/* not Hex characters */
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
/* 0-9 */
0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20,
/* A-F */
20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
/* not Hex characters */
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
/* a-f */
20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
/* not Hex characters (upper 128 characters) */
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
};
uint8_t*
bytes_flip_endian(const uint8_t *bytebuf, const PCSCHEMA *schema, uint32_t npoints)
bytes_from_hexbytes(const char *hexbuf, size_t hexsize)
{
uint8_t *buf = NULL;
register uint8_t h1, h2;
int i;
if( hexsize % 2 )
pcerror("Invalid hex string, length (%d) has to be a multiple of two!", hexsize);
buf = pcalloc(hexsize/2);
if( ! buf )
pcerror("Unable to allocate memory buffer.");
for( i = 0; i < hexsize/2; i++ )
{
h1 = hex2char[(int)hexbuf[2*i]];
h2 = hex2char[(int)hexbuf[2*i+1]];
if( h1 > 15 )
pcerror("Invalid hex character (%c) encountered", hexbuf[2*i]);
if( h2 > 15 )
pcerror("Invalid hex character (%c) encountered", hexbuf[2*i+1]);
/* First character is high bits, second is low bits */
buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F);
}
return buf;
}
char*
hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize)
{
char *buf = pcalloc(2*bytesize + 1); /* 2 chars per byte + null terminator */
int i;
char *ptr = buf;
for ( i = 0; i < bytesize; i++ )
{
int incr = snprintf(ptr, 3, "%02X", bytebuf[i]);
if ( incr < 0 )
{
pcerror("write failure in hexbytes_from_bytes");
return NULL;
}
ptr += incr;
}
return buf;
}
char
machine_endian(void)
{
static int check_int = 1; /* dont modify this!!! */
return *((char *) &check_int); /* 0 = big endian | xdr,
* 1 = little endian | ndr
*/
}
int32_t
int32_flip_endian(int32_t val)
{
int i;
uint8_t tmp;
uint8_t b[4];
memcpy(b, &val, 4);
for ( i = 0; i < 2; i++ )
{
tmp = b[i];
b[i] = b[3-i];
b[3-i] = tmp;
}
memcpy(&val, b, 4);
return val;
}
int16_t
int16_flip_endian(int16_t val)
{
int i;
uint8_t tmp;
uint8_t b[2];
memcpy(b, &val, 2);
tmp = b[0];
b[0] = b[1];
b[1] = tmp;
memcpy(&val, b, 2);
return val;
}
uint32_t
wkb_get_pcid(uint8_t *wkb)
{
/* We expect the bytes to be in WKB format for PCPOINT/PCPATCH */
/* byte 0: endian */
/* byte 1-4: pcid */
/* ...data... */
uint32_t pcid;
memcpy(&pcid, wkb + 1, 4);
if ( wkb[0] != machine_endian() )
{
pcid = int32_flip_endian(pcid);
}
return pcid;
}
uint32_t
wkb_get_compression(uint8_t *wkb)
{
/* We expect the bytes to be in WKB format for PCPATCH */
/* byte 0: endian */
/* byte 1-4: pcid */
/* byte 5-8: compression */
/* ...data... */
uint32_t compression;
memcpy(&compression, wkb+1+4, 4);
if ( wkb[0] != machine_endian() )
{
compression = int32_flip_endian(compression);
}
return compression;
}
uint32_t
wkb_get_npoints(uint8_t *wkb)
{
/* We expect the bytes to be in WKB format for PCPATCH */
/* byte 0: endian */
/* byte 1-4: pcid */
/* byte 5-8: compression */
/* byte 9-12: compression */
/* ...data... */
uint32_t npoints;
memcpy(&npoints, wkb+1+4+4, 4);
if ( wkb[0] != machine_endian() )
{
npoints = int32_flip_endian(npoints);
}
return npoints;
}
uint8_t*
uncompressed_bytes_flip_endian(const uint8_t *bytebuf, const PCSCHEMA *schema, uint32_t npoints)
{
int i, j, k;
size_t bufsize = schema->size * npoints;

View File

View File

@ -4,11 +4,15 @@
/* In/out functions */
Datum pcpoint_in(PG_FUNCTION_ARGS);
Datum pcpoint_out(PG_FUNCTION_ARGS);
Datum pcpatch_in(PG_FUNCTION_ARGS);
Datum pcpatch_out(PG_FUNCTION_ARGS);
/* Other SQL functions */
Datum PC_SchemaIsValid(PG_FUNCTION_ARGS);
Datum PC_SchemaGetNDims(PG_FUNCTION_ARGS);
Datum PC_MakePointFromArray(PG_FUNCTION_ARGS);
Datum PC_PointAsText(PG_FUNCTION_ARGS);
Datum PC_PatchAsText(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pcpoint_in);
Datum pcpoint_in(PG_FUNCTION_ARGS)
@ -51,18 +55,66 @@ Datum pcpoint_out(PG_FUNCTION_ARGS)
{
PCPOINT *pcpt = NULL;
SERIALIZED_POINT *serpt = NULL;
uint8_t *wkb = NULL;
size_t wkb_size = 0;
char *hexwkb = NULL;
serpt = (SERIALIZED_POINT*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
pcpt = pc_point_deserialize(serpt);
wkb = wkb_from_point(pcpt, &wkb_size);
hexwkb = hexbytes_from_bytes(wkb, wkb_size);
hexwkb = pc_point_to_hexwkb(pcpt);
pc_point_free(pcpt);
PG_RETURN_CSTRING(hexwkb);
}
PG_FUNCTION_INFO_V1(pcpatch_in);
Datum pcpatch_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
/* Datum geog_oid = PG_GETARG_OID(1); Not needed. */
int32 pc_typmod = -1;
PCPATCH *patch;
SERIALIZED_PATCH *serpatch;
if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) )
{
pc_typmod = PG_GETARG_INT32(2);
}
/* Empty string. */
if ( str[0] == '\0' )
{
ereport(ERROR,(errmsg("pcpatch parse error - empty string")));
}
/* Binary or text form? Let's find out. */
if ( str[0] == '0' )
{
/* Hex-encoded binary */
patch = pc_patch_from_hexwkb(str, strlen(str));
serpatch = pc_patch_serialize(patch);
pc_patch_free(patch);
}
else
{
ereport(ERROR,(errmsg("parse error - support for text format not yet implemented")));
}
PG_RETURN_POINTER(serpatch);
}
PG_FUNCTION_INFO_V1(pcpatch_out);
Datum pcpatch_out(PG_FUNCTION_ARGS)
{
PCPATCH *patch = NULL;
SERIALIZED_PATCH *serpatch = NULL;
char *hexwkb = NULL;
serpatch = (SERIALIZED_PATCH*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
patch = pc_patch_deserialize(serpatch);
hexwkb = pc_patch_to_hexwkb(patch);
pc_patch_free(patch);
PG_RETURN_CSTRING(hexwkb);
}
PG_FUNCTION_INFO_V1(PC_SchemaIsValid);
Datum PC_SchemaIsValid(PG_FUNCTION_ARGS)
{
@ -79,6 +131,7 @@ Datum PC_SchemaIsValid(PG_FUNCTION_ARGS)
}
valid = pc_schema_is_valid(schema);
pc_schema_free(schema);
PG_RETURN_BOOL(valid);
}
@ -141,5 +194,40 @@ Datum PC_MakePointFromArray(PG_FUNCTION_ARGS)
serpt = pc_point_serialize(pt);
pc_point_free(pt);
pc_schema_free(schema);
PG_RETURN_POINTER(serpt);
}
}
PG_FUNCTION_INFO_V1(PC_PointAsText);
Datum PC_PointAsText(PG_FUNCTION_ARGS)
{
SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0);
text *txt;
char *str;
PCPOINT *pt = pc_point_deserialize(serpt);
if ( ! pt )
PG_RETURN_NULL();
str = pc_point_to_string(pt);
pc_point_free(pt);
txt = cstring_to_text(str);
pfree(str);
PG_RETURN_TEXT_P(txt);
}
PG_FUNCTION_INFO_V1(PC_PatchAsText);
Datum PC_PatchAsText(PG_FUNCTION_ARGS)
{
SERIALIZED_PATCH *serpatch = PG_GETARG_SERPATCH_P(0);
text *txt;
char *str;
PCPATCH *patch = pc_patch_deserialize(serpatch);
if ( ! patch )
PG_RETURN_NULL();
str = pc_patch_to_string(patch);
pc_patch_free(patch);
txt = cstring_to_text(str);
pfree(str);
PG_RETURN_TEXT_P(txt);
}

View File

@ -93,207 +93,71 @@ _PG_fini(void)
/**********************************************************************************
* WKB AND ENDIANESS UTILITIES
* PCPOINT WKB Handling
*/
/* Our static character->number map. Anything > 15 is invalid */
static uint8_t hex2char[256] = {
/* not Hex characters */
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
/* 0-9 */
0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20,
/* A-F */
20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
/* not Hex characters */
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
/* a-f */
20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
/* not Hex characters (upper 128 characters) */
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
};
uint8_t*
bytes_from_hexbytes(const char *hexbuf, size_t hexsize)
{
uint8_t *buf = NULL;
register uint8_t h1, h2;
int i;
if( hexsize % 2 )
pcerror("Invalid hex string, length (%d) has to be a multiple of two!", hexsize);
buf = palloc(hexsize/2);
if( ! buf )
pcerror("Unable to allocate memory buffer.");
for( i = 0; i < hexsize/2; i++ )
{
h1 = hex2char[(int)hexbuf[2*i]];
h2 = hex2char[(int)hexbuf[2*i+1]];
if( h1 > 15 )
pcerror("Invalid hex character (%c) encountered", hexbuf[2*i]);
if( h2 > 15 )
pcerror("Invalid hex character (%c) encountered", hexbuf[2*i+1]);
/* First character is high bits, second is low bits */
buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F);
}
return buf;
}
char*
hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize)
{
char *buf = palloc(2*bytesize + 1); /* 2 chars per byte + null terminator */
int i;
char *ptr = buf;
for ( i = 0; i < bytesize; i++ )
{
int incr = snprintf(ptr, 3, "%02X", bytebuf[i]);
if ( incr < 0 )
{
pcerror("write failure in hexbytes_from_bytes");
return NULL;
}
ptr += incr;
}
return buf;
}
char
machine_endian(void)
{
static int check_int = 1; /* dont modify this!!! */
return *((char *) &check_int); /* 0 = big endian | xdr,
* 1 = little endian | ndr
*/
}
static int32_t
int32_flip_endian(int32_t val)
{
int i;
uint8_t tmp;
uint8_t b[4];
memcpy(b, &val, 4);
for ( i = 0; i < 2; i++ )
{
tmp = b[i];
b[i] = b[3-i];
b[3-i] = tmp;
}
memcpy(&val, b, 4);
return val;
}
uint32_t
wkb_get_pcid(uint8_t *wkb)
{
/* We expect the bytes to be in WKB format for PCPOINT/PCPATCH */
/* byte 0: endian */
/* byte 1-4: pcid */
/* ...data... */
uint8_t wkb_endian = wkb[0];
uint32_t pcid;
memcpy(&pcid, wkb + 1, 4);
if ( wkb_endian != machine_endian() )
{
pcid = int32_flip_endian(pcid);
}
return pcid;
}
PCPOINT *
pc_point_from_hexwkb(const char *hexwkb, size_t hexlen)
{
PCPOINT *pt;
PCSCHEMA *schema;
uint32_t pcid;
uint8_t *wkb = bytes_from_hexbytes(hexwkb, hexlen);
size_t wkblen = hexlen/2;
pt = pc_point_from_wkb(wkb, wkblen);
pcid = wkb_get_pcid(wkb);
schema = pc_schema_get_by_id(pcid);
pt = pc_point_from_wkb(schema, wkb, wkblen);
pfree(wkb);
return pt;
}
PCPOINT *
pc_point_from_wkb(uint8_t *wkb, size_t wkblen)
char *
pc_point_to_hexwkb(const PCPOINT *pt)
{
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uchar[]: data (interpret relative to pcid)
*/
const size_t hdrsz = 1+4; /* endian + pcid */
uint8_t wkb_endian;
uint32_t pcid;
uint8_t *data;
uint8_t *wkb;
size_t wkb_size;
char *hexwkb;
wkb = pc_point_to_wkb(pt, &wkb_size);
hexwkb = hexbytes_from_bytes(wkb, wkb_size);
pfree(wkb);
return hexwkb;
}
/**********************************************************************************
* PCPATCH WKB Handling
*/
PCPATCH *
pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen)
{
PCPATCH *patch;
PCSCHEMA *schema;
PCPOINT *pt;
if ( ! wkblen )
{
elog(ERROR, "pc_point_from_wkb: zero length wkb");
}
wkb_endian = wkb[0];
pcid = wkb_get_pcid(wkb);
uint32_t pcid;
uint8_t *wkb = bytes_from_hexbytes(hexwkb, hexlen);
size_t wkblen = hexlen/2;
pcid = wkb_get_pcid(wkb);
schema = pc_schema_get_by_id(pcid);
if ( (wkblen-hdrsz) != schema->size )
{
elog(ERROR, "pc_point_from_wkb: wkb size inconsistent with schema size");
}
if ( wkb_endian != machine_endian() )
{
/* bytes_flip_endian creates a flipped copy */
data = bytes_flip_endian(wkb+hdrsz, schema, 1);
}
else
{
data = palloc(schema->size);
memcpy(data, wkb+hdrsz, wkblen-hdrsz);
}
pt = pc_point_from_data_rw(schema, data);
return pt;
patch = pc_patch_from_wkb(schema, wkb, wkblen);
pfree(wkb);
return patch;
}
uint8_t *
wkb_from_point(const PCPOINT *pt, size_t *wkbsize)
char *
pc_patch_to_hexwkb(const PCPATCH *patch)
{
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uchar[]: data (interpret relative to pcid)
*/
char endian = machine_endian();
size_t size = 1 + 4 + pt->schema->size;
uint8_t *wkb = palloc(size);
wkb[0] = endian; /* Write endian flag */
memcpy(wkb + 1, &(pt->schema->pcid), 4); /* Write PCID */
memcpy(wkb + 5, pt->data, pt->schema->size); /* Write data */
*wkbsize = size;
return wkb;
uint8_t *wkb;
size_t wkb_size;
char *hexwkb;
wkb = pc_patch_to_wkb(patch, &wkb_size);
hexwkb = hexbytes_from_bytes(wkb, wkb_size);
pfree(wkb);
return hexwkb;
}
/**********************************************************************************
* PCID <=> PCSCHEMA translation via POINTCLOUD_FORMATS
*/
@ -409,43 +273,49 @@ pc_point_deserialize(const SERIALIZED_POINT *serpt)
return pcpt;
}
SERIALIZED_PATCH *
pc_patch_serialize(const PCPATCH *pcpch)
{
size_t serpch_size = sizeof(SERIALIZED_PATCH) - 1 + pcpch->schema->size * pcpch->npoints;
SERIALIZED_PATCH *serpch = palloc(serpch_size);
serpch->pcid = pcpch->schema->pcid;
serpch->npoints = pcpch->npoints;
serpch->xmin = pcpch->xmin;
serpch->ymin = pcpch->ymin;
serpch->xmax = pcpch->xmax;
serpch->ymax = pcpch->ymax;
memcpy(serpch->data, pcpch->data, pcpch->npoints * pcpch->schema->size);
size_t serpch_size;
PCPATCH *patch;
SERIALIZED_PATCH *serpch;
/* Compress uncompressed patches before saving */
patch = pc_patch_compress(pcpch);
/* Allocate */
serpch_size = sizeof(SERIALIZED_PATCH) - 1 + patch->datasize;
serpch = palloc(serpch_size);
/* Copy */
serpch->pcid = patch->schema->pcid;
serpch->npoints = patch->npoints;
serpch->xmin = patch->xmin;
serpch->ymin = patch->ymin;
serpch->xmax = patch->xmax;
serpch->ymax = patch->ymax;
memcpy(serpch->data, patch->data, patch->datasize);
SET_VARSIZE(serpch, serpch_size);
return serpch;
}
PCPATCH *
pc_patch_deserlialize(const SERIALIZED_PATCH *serpatch)
pc_patch_deserialize(const SERIALIZED_PATCH *serpatch)
{
PCPATCH *patch;
PCSCHEMA *schema = pc_schema_get_by_id(serpatch->pcid);
/* on-disk size - size:int32 - pcid:int32 - npoints:int32 - 4*minmax:double = patch data size */
size_t pgsize = VARSIZE(serpatch) - 3*4 - 4*8;
if ( schema->size*serpatch->npoints != pgsize )
{
elog(ERROR, "schema size and disk size mismatch, repair the schema");
return NULL;
}
/* Reference the external data */
patch = pcalloc(sizeof(PCPATCH));
patch->data = (uint8_t*)serpatch->data;
/* Set up basic info */
patch->schema = schema;
patch->readonly = true;
patch->compressed = true;
patch->npoints = serpatch->npoints;
patch->maxpoints = 0;
patch->xmin = serpatch->xmin;
patch->ymin = serpatch->ymin;
patch->xmax = serpatch->xmax;

View File

@ -14,6 +14,7 @@
#define POINTCLOUD_FORMATS_SRID "srid"
#define PG_GETARG_SERPOINT_P(datum) (SERIALIZED_POINT*)PG_DETOAST_DATUM(PG_GETARG_DATUM(datum))
#define PG_GETARG_SERPATCH_P(datum) (SERIALIZED_PATCH*)PG_DETOAST_DATUM(PG_GETARG_DATUM(datum))
/**
* Serialized point type for clouds. Variable length, because there can be
@ -63,23 +64,21 @@ SERIALIZED_POINT* pc_point_serialize(const PCPOINT *pcpt);
/** Turn a byte buffer into a PCPOINT for processing */
PCPOINT* pc_point_deserialize(const SERIALIZED_POINT *serpt);
/** Returns 1 for little (NDR) and 0 for big (XDR) */
char machine_endian(void);
/** Turn a binary buffer into a hex string */
char* hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize);
/** Create a new readwrite PCPOINT from a hex byte array */
PCPOINT* pc_point_from_wkb(uint8_t *wkb, size_t wkbsize);
/** Create a new readwrite PCPOINT from a hex string */
PCPOINT* pc_point_from_hexwkb(const char *hexwkb, size_t hexlen);
/** Returns serialized form of point */
uint8_t* wkb_from_point(const PCPOINT *pt, size_t *wkbsize);
/** Create a hex representation of a PCPOINT */
char* pc_point_to_hexwkb(const PCPOINT *pt);
/** Turn a PCPATCH into a byte buffer suitable for saving in PgSQL */
SERIALIZED_PATCH* pc_patch_serialize(const PCPATCH *pcpch);
SERIALIZED_PATCH* pc_patch_serialize(const PCPATCH *patch);
/** Turn a byte buffer into a PCPATCH for processing */
PCPATCH* pc_patch_deserlialize(const SERIALIZED_PATCH *serpatch);
PCPATCH* pc_patch_deserialize(const SERIALIZED_PATCH *serpatch);
/** Create a new readwrite PCPATCH from a hex string */
PCPATCH* pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen);
/** Create a hex representation of a PCPOINT */
char* pc_patch_to_hexwkb(const PCPATCH *patch);

View File

@ -63,8 +63,47 @@ CREATE OR REPLACE FUNCTION PC_MakePoint(pcid integer, vals float8[])
RETURNS pcpoint AS 'MODULE_PATHNAME', 'PC_MakePointFromArray'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION PC_AsText(p pcpoint)
RETURNS text AS 'MODULE_PATHNAME', 'PC_PointAsText'
LANGUAGE 'c' IMMUTABLE STRICT;
-------------------------------------------------------------------
-- PCPATCH
-------------------------------------------------------------------
CREATE OR REPLACE FUNCTION pcpatch_in(cstring)
RETURNS pcpatch AS 'MODULE_PATHNAME', 'pcpatch_in'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION pcpatch_out(pcpatch)
RETURNS cstring AS 'MODULE_PATHNAME', 'pcpatch_out'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE TYPE pcpatch (
internallength = variable,
input = pcpatch_in,
output = pcpatch_out,
-- send = geometry_send,
-- receive = geometry_recv,
-- typmod_in = geometry_typmod_in,
-- typmod_out = geometry_typmod_out,
-- delimiter = ':',
-- alignment = double,
-- analyze = geometry_analyze,
storage = main
);
CREATE OR REPLACE FUNCTION PC_AsText(p pcpatch)
RETURNS text AS 'MODULE_PATHNAME', 'PC_PatchAsText'
LANGUAGE 'c' IMMUTABLE STRICT;
-------------------------------------------------------------------
-- SAMPLE DATA
--
-- Sample data
INSERT INTO pointcloud_formats (pcid, srid, schema) VALUES (1, 4326, '<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<pc:dimension>

0
pgsql/sql/pointcloud.sql Normal file
View File