in src/apps/filters/h3.c [1448:1608]
H3Error polygonStringToGeoPolygon(FILE *fp, char *polygonString,
GeoPolygon *out) {
// There are two kinds of valid input, an array of arrays of lat, lng values
// defining a singular polygon loop, or an array of array of arrays of lat,
// lng values defining a polygon and zero or more holes. Which kind of input
// this is can be determined by the `[` depth reached before the first
// floating point number is found. This means either 2 or 3 `[`s at the
// beginning are valid, and nothing more than that.
int8_t maxDepth = 0;
int8_t curDepth = 0;
int64_t numVerts = 6;
int64_t curVert = 0;
int64_t curLoop = 0;
LatLng *verts = calloc(numVerts, sizeof(LatLng));
int strPos = 0;
while (polygonString[strPos] != 0) {
// Load more of the file if we've hit our buffer limit
if (strPos >= BUFFER_SIZE && fp != 0) {
int result = fread(polygonString, 1, BUFFER_SIZE, fp);
strPos = 0;
// If we didn't get any data from the file, we're done.
if (result == 0) {
break;
}
}
// Try to match `[` first
if (polygonString[strPos] == '[') {
curDepth++;
if (curDepth > maxDepth) {
maxDepth = curDepth;
}
if (curDepth > 4) {
// This is beyond the depth for a valid input, so we abort early
free(verts);
return E_FAILED;
}
strPos++;
continue;
}
// Similar matching for `]`
if (polygonString[strPos] == ']') {
curDepth--;
if (curDepth < 0) {
break;
}
strPos++;
// We may need to set up a new geoloop at this point. If the
// curDepth <= maxDepth - 2 then we increment the curLoop and get a
// new one set up.
if (curDepth <= maxDepth - 2) {
if (curLoop == 0) {
out->geoloop.verts = verts;
out->geoloop.numVerts = curVert;
} else {
GeoLoop *holes = calloc(out->numHoles + 1, sizeof(GeoLoop));
if (holes == 0) {
return E_MEMORY_ALLOC;
}
for (int i = 0; i < out->numHoles; i++) {
holes[i].numVerts = out->holes[i].numVerts;
holes[i].verts = out->holes[i].verts;
}
free(out->holes);
holes[out->numHoles].verts = verts;
holes[out->numHoles].numVerts = curVert;
out->holes = holes;
out->numHoles++;
}
curLoop++;
curVert = 0;
numVerts = 6;
verts = calloc(numVerts, sizeof(LatLng));
}
continue;
}
// Try to grab a floating point value followed by optional whitespace, a
// comma, and more optional whitespace, and a second floating point
// value, then store the lat, lng pair
double lat, lng;
int count = 0;
// Must grab the closing ] or we might accidentally parse a partial
// float across buffer boundaries
char closeBracket[2] = "]";
int result = sscanf(polygonString + strPos, "%lf%*[, \n]%lf%1[]]%n",
&lat, &lng, &closeBracket[0], &count);
if (count > 0 && result == 3) {
verts[curVert].lat = H3_EXPORT(degsToRads)(lat);
verts[curVert].lng = H3_EXPORT(degsToRads)(lng);
curVert++;
// Create a new vert buffer, copy the old buffer, and free it, if
// necessary
if (curVert == numVerts) {
LatLng *newVerts = calloc(numVerts * 2, sizeof(LatLng));
for (int i = 0; i < numVerts; i++) {
newVerts[i].lat = verts[i].lat;
newVerts[i].lng = verts[i].lng;
}
free(verts);
verts = newVerts;
numVerts *= 2;
}
strPos += count;
curDepth--;
continue;
}
// Check for whitespace and skip it if we reach this point.
if (polygonString[strPos] == ',' || polygonString[strPos] == ' ' ||
polygonString[strPos] == '\n' || polygonString[strPos] == '\t' ||
polygonString[strPos] == '\r') {
strPos++;
continue;
}
if (strPos != 0 && fp != 0) {
// Scan the remaining of the current buffer for `0`. If not
// found, then we grab a new chunk and append to the remainder
// of the existing buffer. Otherwise, just skip unknown
// characters.
bool endOfFile = false;
for (int i = strPos; i <= BUFFER_SIZE; i++) {
if (polygonString[strPos] == 0) {
endOfFile = true;
break;
}
}
if (endOfFile) {
strPos++;
} else {
// Move the end of this buffer to the beginning
for (int i = strPos; i <= BUFFER_SIZE; i++) {
polygonString[i - strPos] = polygonString[i];
}
// Set the string position to the end of the buffer
strPos = BUFFER_SIZE - strPos;
// Grab up to the remaining size of the buffer and fill it
// into the file
// Did you know that if the amount you want to read from
// the file buffer is larger than the buffer itself,
// fread can choose to give you squat and not the
// remainder of the file as you'd expect from the
// documentation? This was a surprise to me and a
// significant amount of wasted time to figure out how
// to tackle. C leaves *way* too much as undefined
// behavior to be nice to work with...
int64_t i = 0;
int result;
do {
result = fread(polygonString + strPos + i, 1, 1, fp);
i++;
} while (i < BUFFER_SIZE - strPos && result != 0);
if (result == 0) {
polygonString[strPos + i - 1] = 0;
}
strPos = 0;
}
} else {
strPos++;
}
}
free(verts);
return E_SUCCESS;
}