Fix #771: properly check return value of mmap
authorColin Clark <colin.clark@cclark.uk>
Tue, 2 Jun 2020 14:37:12 +0000 (15:37 +0100)
committerColin Clark <colin.clark@cclark.uk>
Tue, 2 Jun 2020 14:37:12 +0000 (15:37 +0100)
https://github.com/BestImageViewer/geeqie/pull/771

Updates from ZoneDetect project:
https://github.com/BertoldVdb/ZoneDetect

src/zonedetect.c
src/zonedetect.h

index 8a7b0ea..47fe37b 100644 (file)
  */
 
 #include <assert.h>
+#include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
 #if defined(_MSC_VER) || defined(__MINGW32__)
-  #include <windows.h>
+#include <windows.h>
 #else
-  #include <errno.h>
-  #include <sys/mman.h>
-  #include <fcntl.h>
-  #include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
 #endif
 
 #include "zonedetect.h"
@@ -68,6 +69,7 @@ struct ZoneDetectOpaque {
     off_t length;
 #endif
 
+    uint8_t closeType;
     uint8_t *mapping;
 
     uint8_t tableType;
@@ -95,38 +97,44 @@ static int32_t ZDFloatToFixedPoint(float input, float scale, unsigned int precis
     return (int32_t)(inputScaled * (float)(1 << (precision - 1)));
 }
 
-static unsigned int ZDDecodeVariableLengthUnsigned(const ZoneDetect *library, uint32_t *index, uint32_t *result)
+static float ZDFixedPointToFloat(int32_t input, float scale, unsigned int precision)
+{
+    const float value = (float)input / (float)(1 << (precision - 1));
+    return value * scale;
+}
+
+static unsigned int ZDDecodeVariableLengthUnsigned(const ZoneDetect *library, uint32_t *index, uint64_t *result)
 {
     if(*index >= (uint32_t)library->length) {
         return 0;
     }
 
-    uint32_t value = 0;
+    uint64_t value = 0;
     unsigned int i = 0;
 #if defined(_MSC_VER)
     __try {
 #endif
-    uint8_t *const buffer = library->mapping + *index;
-    uint8_t *const bufferEnd = library->mapping + library->length - 1;
+        uint8_t *const buffer = library->mapping + *index;
+        uint8_t *const bufferEnd = library->mapping + library->length - 1;
 
-    unsigned int shift = 0;
-    while(1) {
-        value |= (uint32_t)((buffer[i] & UINT8_C(0x7F)) << shift);
-        shift += 7u;
+        unsigned int shift = 0;
+        while(1) {
+            value |= ((((uint64_t)buffer[i]) & UINT8_C(0x7F)) << shift);
+            shift += 7u;
 
-        if(!(buffer[i] & UINT8_C(0x80))) {
-            break;
-        }
+            if(!(buffer[i] & UINT8_C(0x80))) {
+                break;
+            }
 
-        i++;
-        if(buffer + i > bufferEnd) {
-            return 0;
+            i++;
+            if(buffer + i > bufferEnd) {
+                return 0;
+            }
         }
-    }
 #if defined(_MSC_VER)
     } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
-      ? EXCEPTION_EXECUTE_HANDLER
-      : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
+               ? EXCEPTION_EXECUTE_HANDLER
+               : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
         zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
         return 0;
     }
@@ -138,17 +146,67 @@ static unsigned int ZDDecodeVariableLengthUnsigned(const ZoneDetect *library, ui
     return i;
 }
 
+static unsigned int ZDDecodeVariableLengthUnsignedReverse(const ZoneDetect *library, uint32_t *index, uint64_t *result)
+{
+    uint32_t i = *index;
+
+    if(*index >= (uint32_t)library->length) {
+        return 0;
+    }
+
+#if defined(_MSC_VER)
+    __try {
+#endif
+
+        if(library->mapping[i] & UINT8_C(0x80)) {
+            return 0;
+        }
+
+        if(!i) {
+            return 0;
+        }
+        i--;
+
+        while(library->mapping[i] & UINT8_C(0x80)) {
+            if(!i) {
+                return 0;
+            }
+            i--;
+        }
+
+#if defined(_MSC_VER)
+    } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
+               ? EXCEPTION_EXECUTE_HANDLER
+               : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
+        zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
+        return 0;
+    }
+#endif
+
+    *index = i;
+
+    i++;
+
+    uint32_t i2 = i;
+    return ZDDecodeVariableLengthUnsigned(library, &i2, result);
+}
+
+static int64_t ZDDecodeUnsignedToSigned(uint64_t value)
+{
+    return (value & 1) ? -(int64_t)(value / 2) : (int64_t)(value / 2);
+}
+
 static unsigned int ZDDecodeVariableLengthSigned(const ZoneDetect *library, uint32_t *index, int32_t *result)
 {
-    uint32_t value = 0;
+    uint64_t value = 0;
     const unsigned int retVal = ZDDecodeVariableLengthUnsigned(library, index, &value);
-    *result = (value & 1) ? -(int32_t)(value / 2) : (int32_t)(value / 2);
+    *result = (int32_t)ZDDecodeUnsignedToSigned(value);
     return retVal;
 }
 
 static char *ZDParseString(const ZoneDetect *library, uint32_t *index)
 {
-    uint32_t strLength;
+    uint64_t strLength;
     if(!ZDDecodeVariableLengthUnsigned(library, index, &strLength)) {
         return NULL;
     }
@@ -156,7 +214,7 @@ static char *ZDParseString(const ZoneDetect *library, uint32_t *index)
     uint32_t strOffset = *index;
     unsigned int remoteStr = 0;
     if(strLength >= 256) {
-        strOffset = library->metadataOffset + strLength - 256;
+        strOffset = library->metadataOffset + (uint32_t)strLength - 256;
         remoteStr = 1;
 
         if(!ZDDecodeVariableLengthUnsigned(library, &strOffset, &strLength)) {
@@ -168,28 +226,28 @@ static char *ZDParseString(const ZoneDetect *library, uint32_t *index)
         }
     }
 
-    char *const str = malloc(strLength + 1);
+    char *const str = malloc((size_t)(strLength + 1));
 
     if(str) {
 #if defined(_MSC_VER)
-    __try {
+        __try {
 #endif
-        for(size_t i = 0; i < strLength; i++) {
-            str[i] = (char)(library->mapping[strOffset + i] ^ UINT8_C(0x80));
-        }
+            for(size_t i = 0; i < strLength; i++) {
+                str[i] = (char)(library->mapping[strOffset + i] ^ UINT8_C(0x80));
+            }
 #if defined(_MSC_VER)
-    } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
-      ? EXCEPTION_EXECUTE_HANDLER
-      : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
-        zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
-        return 0;
-    }
+        } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
+                   ? EXCEPTION_EXECUTE_HANDLER
+                   : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
+            zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
+            return 0;
+        }
 #endif
         str[strLength] = 0;
     }
 
     if(!remoteStr) {
-        *index += strLength;
+        *index += (uint32_t)strLength;
     }
 
     return str;
@@ -204,24 +262,24 @@ static int ZDParseHeader(ZoneDetect *library)
 #if defined(_MSC_VER)
     __try {
 #endif
-    if(memcmp(library->mapping, "PLB", 3)) {
-        return -1;
-    }
+        if(memcmp(library->mapping, "PLB", 3)) {
+            return -1;
+        }
 
-    library->tableType = library->mapping[3];
-    library->version   = library->mapping[4];
-    library->precision = library->mapping[5];
-    library->numFields = library->mapping[6];
+        library->tableType = library->mapping[3];
+        library->version   = library->mapping[4];
+        library->precision = library->mapping[5];
+        library->numFields = library->mapping[6];
 #if defined(_MSC_VER)
     } __except(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
-      ? EXCEPTION_EXECUTE_HANDLER
-      : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
+               ? EXCEPTION_EXECUTE_HANDLER
+               : EXCEPTION_CONTINUE_SEARCH) { /* file mapping SEH exception occurred */
         zdError(ZD_E_DB_MAP_EXCEPTION, (int)GetLastError());
         return 0;
     }
 #endif
 
-    if(library->version != 0) {
+    if(library->version >= 2) {
         return -1;
     }
 
@@ -237,15 +295,15 @@ static int ZDParseHeader(ZoneDetect *library)
         return -1;
     }
 
-    uint32_t tmp;
+    uint64_t tmp;
     /* Read section sizes */
     /* By memset: library->bboxOffset = 0 */
 
     if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp)) return -1;
-    library->metadataOffset = tmp + library->bboxOffset;
+    library->metadataOffset = (uint32_t)tmp + library->bboxOffset;
 
     if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp))return -1;
-    library->dataOffset = tmp + library->metadataOffset;
+    library->dataOffset = (uint32_t)tmp + library->metadataOffset;
 
     if(!ZDDecodeVariableLengthUnsigned(library, &index, &tmp)) return -1;
 
@@ -273,32 +331,317 @@ static int ZDPointInBox(int32_t xl, int32_t x, int32_t xr, int32_t yl, int32_t y
     return 0;
 }
 
-static ZDLookupResult ZDPointInPolygon(const ZoneDetect *library, uint32_t polygonIndex, int32_t latFixedPoint, int32_t lonFixedPoint, uint64_t *distanceSqrMin)
+static uint32_t ZDUnshuffle(uint64_t w)
 {
-    uint32_t numVertices;
-    int32_t pointLat = 0, pointLon = 0, diffLat = 0, diffLon = 0, firstLat = 0, firstLon = 0, prevLat = 0, prevLon = 0;
-    lonFixedPoint -= 3;
+    w &=                  0x5555555555555555;
+    w = (w | (w >> 1))  & 0x3333333333333333;
+    w = (w | (w >> 2))  & 0x0F0F0F0F0F0F0F0F;
+    w = (w | (w >> 4))  & 0x00FF00FF00FF00FF;
+    w = (w | (w >> 8))  & 0x0000FFFF0000FFFF;
+    w = (w | (w >> 16)) & 0x00000000FFFFFFFF;
+    return (uint32_t)w;
+}
 
-    /* Read number of vertices */
-    if(!ZDDecodeVariableLengthUnsigned(library, &polygonIndex, &numVertices)) return ZD_LOOKUP_PARSE_ERROR;
-    if(numVertices > 1000000) return ZD_LOOKUP_PARSE_ERROR;
+static void ZDDecodePoint(uint64_t point, int32_t* lat, int32_t* lon)
+{
+    *lat = (int32_t)ZDDecodeUnsignedToSigned(ZDUnshuffle(point));
+    *lon = (int32_t)ZDDecodeUnsignedToSigned(ZDUnshuffle(point >> 1));
+}
 
-    int prevQuadrant = 0, winding = 0;
+struct Reader {
+    const ZoneDetect *library;
+    uint32_t polygonIndex;
+
+    uint64_t numVertices;
+
+    uint8_t done, first;
+    uint32_t referenceStart, referenceEnd;
+    int32_t referenceDirection;
 
-    for(size_t i = 0; i <= (size_t)numVertices; i++) {
-        if(i < (size_t)numVertices) {
-            if(!ZDDecodeVariableLengthSigned(library, &polygonIndex, &diffLat)) return ZD_LOOKUP_PARSE_ERROR;
-            if(!ZDDecodeVariableLengthSigned(library, &polygonIndex, &diffLon)) return ZD_LOOKUP_PARSE_ERROR;
-            pointLat += diffLat;
-            pointLon += diffLon;
-            if(i == 0) {
-                firstLat = pointLat;
-                firstLon = pointLon;
+    int32_t pointLat, pointLon;
+    int32_t firstLat, firstLon;
+};
+
+static void ZDReaderInit(struct Reader *reader, const ZoneDetect *library, uint32_t polygonIndex)
+{
+    memset(reader, 0, sizeof(*reader));
+
+    reader->library = library;
+    reader->polygonIndex = polygonIndex;
+
+    reader->first = 1;
+}
+
+static int ZDReaderGetPoint(struct Reader *reader, int32_t *pointLat, int32_t *pointLon)
+{
+    int32_t diffLat = 0, diffLon = 0;
+
+readNewPoint:
+    if(reader->done > 1) {
+        return 0;
+    }
+
+    if(reader->first && reader->library->version == 0) {
+        if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &reader->numVertices)) return -1;
+        if(!reader->numVertices) return -1;
+    }
+
+    uint8_t referenceDone = 0;
+
+    if(reader->library->version == 1) {
+        uint64_t point = 0;
+
+        if(!reader->referenceDirection) {
+            if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &point)) return -1;
+        } else {
+            if(reader->referenceDirection > 0) {
+                /* Read reference forward */
+                if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->referenceStart, &point)) return -1;
+                if(reader->referenceStart >= reader->referenceEnd) {
+                    referenceDone = 1;
+                }
+            } else if(reader->referenceDirection < 0) {
+                /* Read reference backwards */
+                if(!ZDDecodeVariableLengthUnsignedReverse(reader->library, &reader->referenceStart, &point)) return -1;
+                if(reader->referenceStart <= reader->referenceEnd) {
+                    referenceDone = 1;
+                }
+            }
+        }
+
+        if(!point) {
+            /* This is a special marker, it is not allowed in reference mode */
+            if(reader->referenceDirection) {
+                return -1;
+            }
+
+            uint64_t value;
+            if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, &value)) return -1;
+
+            if(value == 0) {
+                reader->done = 2;
+            } else if(value == 1) {
+                int32_t diff;
+                int64_t start;
+                if(!ZDDecodeVariableLengthUnsigned(reader->library, &reader->polygonIndex, (uint64_t*)&start)) return -1;
+                if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diff)) return -1;
+
+                reader->referenceStart = reader->library->dataOffset+(uint32_t)start;
+                reader->referenceEnd = reader->library->dataOffset+(uint32_t)(start + diff);
+                reader->referenceDirection = diff;
+                if(diff < 0) {
+                    reader->referenceStart--;
+                    reader->referenceEnd--;
+                }
+                goto readNewPoint;
             }
         } else {
-            /* The polygons should be closed, but just in case */
-            pointLat = firstLat;
-            pointLon = firstLon;
+            ZDDecodePoint(point, &diffLat, &diffLon);
+            if(reader->referenceDirection < 0) {
+                diffLat = -diffLat;
+                diffLon = -diffLon;
+            }
+        }
+    }
+
+    if(reader->library->version == 0) {
+        if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diffLat)) return -1;
+        if(!ZDDecodeVariableLengthSigned(reader->library, &reader->polygonIndex, &diffLon)) return -1;
+    }
+
+    if(!reader->done) {
+        reader->pointLat += diffLat;
+        reader->pointLon += diffLon;
+        if(reader->first) {
+            reader->firstLat = reader->pointLat;
+            reader->firstLon = reader->pointLon;
+        }
+    } else {
+        /* Close the polygon (the closing point is not encoded) */
+        reader->pointLat = reader->firstLat;
+        reader->pointLon = reader->firstLon;
+        reader->done = 2;
+    }
+
+    reader->first = 0;
+
+    if(reader->library->version == 0) {
+        reader->numVertices--;
+        if(!reader->numVertices) {
+            reader->done = 1;
+        }
+        if(!diffLat && !diffLon) {
+            goto readNewPoint;
+        }
+    }
+
+    if(referenceDone) {
+        reader->referenceDirection = 0;
+    }
+
+    if(pointLat) {
+        *pointLat = reader->pointLat;
+    }
+
+    if(pointLon) {
+        *pointLon = reader->pointLon;
+    }
+
+    return 1;
+}
+
+static int ZDFindPolygon(const ZoneDetect *library, uint32_t wantedId, uint32_t* metadataIndexPtr, uint32_t* polygonIndexPtr)
+{
+    uint32_t polygonId = 0;
+    uint32_t bboxIndex = library->bboxOffset;
+
+    uint32_t metadataIndex = 0, polygonIndex = 0;
+
+    while(bboxIndex < library->metadataOffset) {
+        uint64_t polygonIndexDelta;
+        int32_t metadataIndexDelta;
+        int32_t tmp;
+        if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
+        if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
+        if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
+        if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &tmp)) break;
+        if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &metadataIndexDelta)) break;
+        if(!ZDDecodeVariableLengthUnsigned(library, &bboxIndex, &polygonIndexDelta)) break;
+
+        metadataIndex += (uint32_t)metadataIndexDelta;
+        polygonIndex += (uint32_t)polygonIndexDelta;
+
+        if(polygonId == wantedId) {
+            if(metadataIndexPtr) {
+                metadataIndex += library->metadataOffset;
+                *metadataIndexPtr = metadataIndex;
+            }
+            if(polygonIndexPtr) {
+                polygonIndex += library->dataOffset;
+                *polygonIndexPtr = polygonIndex;
+            }
+            return 1;
+        }
+
+        polygonId ++;
+    }
+
+    return 0;
+}
+
+static int32_t* ZDPolygonToListInternal(const ZoneDetect *library, uint32_t polygonIndex, size_t* length)
+{
+    struct Reader reader;
+    ZDReaderInit(&reader, library, polygonIndex);
+
+    size_t listLength = 2 * 100;
+    size_t listIndex = 0;
+
+    int32_t* list = malloc(sizeof(int32_t) * listLength);
+    if(!list) {
+        goto fail;
+    }
+
+    while(1) {
+        int32_t pointLat, pointLon;
+        int result = ZDReaderGetPoint(&reader, &pointLat, &pointLon);
+        if(result < 0) {
+            goto fail;
+        } else if(result == 0) {
+            break;
+        }
+
+        if(listIndex >= listLength) {
+            listLength *= 2;
+            if(listLength >= 1048576) {
+                goto fail;
+            }
+
+            list = realloc(list, sizeof(int32_t) * listLength);
+            if(!list) {
+                goto fail;
+            }
+        }
+
+        list[listIndex++] = pointLat;
+        list[listIndex++] = pointLon;
+    }
+
+    if(length) {
+        *length = listIndex;
+    }
+
+    return list;
+
+fail:
+    if(list) {
+        free(list);
+    }
+    return NULL;
+}
+
+float* ZDPolygonToList(const ZoneDetect *library, uint32_t polygonId, size_t* lengthPtr)
+{
+    uint32_t polygonIndex;
+    int32_t* data = NULL;
+    float* flData = NULL;
+
+    if(!ZDFindPolygon(library, polygonId, NULL, &polygonIndex)) {
+        goto fail;
+    }
+
+    size_t length = 0;
+    data = ZDPolygonToListInternal(library, polygonIndex, &length);
+
+    if(!data) {
+        goto fail;
+    }
+
+    flData = malloc(sizeof(float) * length);
+    if(!flData) {
+        goto fail;
+    }
+
+    for(size_t i = 0; i<length; i+= 2) {
+        int32_t lat = data[i];
+        int32_t lon = data[i+1];
+
+        flData[i] = ZDFixedPointToFloat(lat, 90, library->precision);
+        flData[i+1] = ZDFixedPointToFloat(lon, 180, library->precision);
+    }
+
+    if(lengthPtr) {
+        *lengthPtr = length;
+    }
+
+    return flData;
+
+fail:
+    if(data) {
+        free(data);
+    }
+    if(flData) {
+        free(flData);
+    }
+    return NULL;
+}
+
+static ZDLookupResult ZDPointInPolygon(const ZoneDetect *library, uint32_t polygonIndex, int32_t latFixedPoint, int32_t lonFixedPoint, uint64_t *distanceSqrMin)
+{
+    int32_t pointLat, pointLon, prevLat = 0, prevLon = 0;
+    int prevQuadrant = 0, winding = 0;
+
+    uint8_t first = 1;
+
+    struct Reader reader;
+    ZDReaderInit(&reader, library, polygonIndex);
+
+    while(1) {
+        int result = ZDReaderGetPoint(&reader, &pointLat, &pointLon);
+        if(result < 0) {
+            return ZD_LOOKUP_PARSE_ERROR;
+        } else if(result == 0) {
+            break;
         }
 
         /* Check if point is ON the border */
@@ -323,7 +666,7 @@ static ZDLookupResult ZDPointInPolygon(const ZoneDetect *library, uint32_t polyg
             }
         }
 
-        if(i > 0) {
+        if(!first) {
             int windingNeedCompare = 0, lineIsStraight = 0;
             float a = 0, b = 0;
 
@@ -349,16 +692,17 @@ static ZDLookupResult ZDPointInPolygon(const ZoneDetect *library, uint32_t polyg
                 b = (float)pointLat - a * (float)pointLon;
             }
 
+            int onStraight = ZDPointInBox(pointLat, latFixedPoint, prevLat, pointLon, lonFixedPoint, prevLon);
+            if(lineIsStraight && (onStraight || windingNeedCompare)) {
+                if(distanceSqrMin) *distanceSqrMin = 0;
+                return ZD_LOOKUP_ON_BORDER_SEGMENT;
+            }
+
             /* Jumped two quadrants. */
             if(windingNeedCompare) {
-                if(lineIsStraight) {
-                    if(distanceSqrMin) *distanceSqrMin = 0;
-                    return ZD_LOOKUP_ON_BORDER_SEGMENT;
-                }
-
                 /* Check if the target is on the border */
                 const int32_t intersectLon = (int32_t)(((float)latFixedPoint - b) / a);
-                if(intersectLon == lonFixedPoint) {
+                if(intersectLon >= lonFixedPoint-1 && intersectLon <= lonFixedPoint+1) {
                     if(distanceSqrMin) *distanceSqrMin = 0;
                     return ZD_LOOKUP_ON_BORDER_SEGMENT;
                 }
@@ -413,7 +757,8 @@ static ZDLookupResult ZDPointInPolygon(const ZoneDetect *library, uint32_t polyg
         prevQuadrant = quadrant;
         prevLat = pointLat;
         prevLon = pointLon;
-    }
+        first = 0;
+    };
 
     if(winding == -4) {
         return ZD_LOOKUP_IN_ZONE;
@@ -443,19 +788,51 @@ void ZDCloseDatabase(ZoneDetect *library)
             free(library->notice);
         }
 
+        if(library->closeType == 0) {
 #if defined(_MSC_VER) || defined(__MINGW32__)
-        if(!UnmapViewOfFile(library->mapping)) zdError(ZD_E_DB_MUNMAP_MSVIEW, (int)GetLastError());
-        if(!CloseHandle(library->fdMap))       zdError(ZD_E_DB_MUNMAP       , (int)GetLastError());
-        if(!CloseHandle(library->fd))          zdError(ZD_E_DB_CLOSE        , (int)GetLastError());
+            if(library->mapping && !UnmapViewOfFile(library->mapping)) zdError(ZD_E_DB_MUNMAP_MSVIEW, (int)GetLastError());
+            if(library->fdMap && !CloseHandle(library->fdMap))         zdError(ZD_E_DB_MUNMAP, (int)GetLastError());
+            if(library->fd && !CloseHandle(library->fd))               zdError(ZD_E_DB_CLOSE, (int)GetLastError());
 #else
-        if(library->mapping && munmap(library->mapping, (size_t)(library->length))) zdError(ZD_E_DB_MUNMAP, errno);
-        if(library->fd >= 0 && close(library->fd))                                  zdError(ZD_E_DB_CLOSE , errno);
+            if(library->mapping && munmap(library->mapping, (size_t)(library->length))) zdError(ZD_E_DB_MUNMAP, errno);
+            if(library->fd >= 0 && close(library->fd))                                  zdError(ZD_E_DB_CLOSE, errno);
 #endif
+        }
 
         free(library);
     }
 }
 
+ZoneDetect *ZDOpenDatabaseFromMemory(void* buffer, size_t length)
+{
+    ZoneDetect *const library = malloc(sizeof *library);
+
+    if(library) {
+        memset(library, 0, sizeof(*library));
+        library->closeType = 1;
+        library->length = (long int)length;
+
+        if(library->length <= 0) {
+            zdError(ZD_E_DB_SEEK, errno);
+            goto fail;
+        }
+
+        library->mapping = buffer;
+
+        /* Parse the header */
+        if(ZDParseHeader(library)) {
+            zdError(ZD_E_PARSE_HEADER, 0);
+            goto fail;
+        }
+    }
+
+    return library;
+
+fail:
+    ZDCloseDatabase(library);
+    return NULL;
+}
+
 ZoneDetect *ZDOpenDatabase(const char *path)
 {
     ZoneDetect *const library = malloc(sizeof *library);
@@ -496,14 +873,14 @@ ZoneDetect *ZDOpenDatabase(const char *path)
         }
 
         library->length = lseek(library->fd, 0, SEEK_END);
-        if(library->length <= 0) {
+        if(library->length <= 0 || library->length > 50331648) {
             zdError(ZD_E_DB_SEEK, errno);
             goto fail;
         }
         lseek(library->fd, 0, SEEK_SET);
 
         library->mapping = mmap(NULL, (size_t)library->length, PROT_READ, MAP_PRIVATE | MAP_FILE, library->fd, 0);
-        if(!library->mapping) {
+        if(library->mapping == MAP_FAILED) {
             zdError(ZD_E_DB_MMAP, errno);
             goto fail;
         }
@@ -540,9 +917,11 @@ ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, floa
         return NULL;
     }
 
+    uint32_t polygonId = 0;
+
     while(bboxIndex < library->metadataOffset) {
         int32_t minLat, minLon, maxLat, maxLon, metadataIndexDelta;
-        uint32_t polygonIndexDelta;
+        uint64_t polygonIndexDelta;
         if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &minLat)) break;
         if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &minLon)) break;
         if(!ZDDecodeVariableLengthSigned(library, &bboxIndex, &maxLat)) break;
@@ -551,17 +930,13 @@ ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, floa
         if(!ZDDecodeVariableLengthUnsigned(library, &bboxIndex, &polygonIndexDelta)) break;
 
         metadataIndex += (uint32_t)metadataIndexDelta;
-        polygonIndex += polygonIndexDelta;
+        polygonIndex += (uint32_t)polygonIndexDelta;
 
         if(latFixedPoint >= minLat) {
             if(latFixedPoint <= maxLat &&
                     lonFixedPoint >= minLon &&
                     lonFixedPoint <= maxLon) {
 
-                /* Indices valid? */
-                if(library->metadataOffset + metadataIndex >= library->dataOffset) continue;
-                if(library->dataOffset + polygonIndex >= (uint32_t)library->length) continue;
-
                 const ZDLookupResult lookupResult = ZDPointInPolygon(library, library->dataOffset + polygonIndex, latFixedPoint, lonFixedPoint, (safezone) ? &distanceSqrMin : NULL);
                 if(lookupResult == ZD_LOOKUP_PARSE_ERROR) {
                     break;
@@ -570,6 +945,7 @@ ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, floa
 
                     if(newResults) {
                         results = newResults;
+                        results[numResults].polygonId = polygonId;
                         results[numResults].metaId = metadataIndex;
                         results[numResults].numFields = library->numFields;
                         results[numResults].fieldNames = library->fieldNames;
@@ -585,6 +961,8 @@ ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, floa
             /* The data is sorted along minLat */
             break;
         }
+
+        polygonId++;
     }
 
     /* Clean up results */
@@ -713,19 +1091,30 @@ const char *ZDLookupResultToString(ZDLookupResult result)
 const char *ZDGetErrorString(int errZD)
 {
     switch ((enum ZDInternalError)errZD) {
-        default: assert(0);
-        case ZD_OK                : return "";
-        case ZD_E_DB_OPEN         : return ZD_E_COULD_NOT("open database file");
-        case ZD_E_DB_SEEK         : return ZD_E_COULD_NOT("retrieve database file size");
-        case ZD_E_DB_MMAP         : return ZD_E_COULD_NOT("map database file to system memory");
+        default:
+            assert(0);
+        case ZD_OK                :
+            return "";
+        case ZD_E_DB_OPEN         :
+            return ZD_E_COULD_NOT("open database file");
+        case ZD_E_DB_SEEK         :
+            return ZD_E_COULD_NOT("retrieve database file size");
+        case ZD_E_DB_MMAP         :
+            return ZD_E_COULD_NOT("map database file to system memory");
 #if defined(_MSC_VER) || defined(__MINGW32__)
-        case ZD_E_DB_MMAP_MSVIEW  : return ZD_E_COULD_NOT("open database file view");
-        case ZD_E_DB_MAP_EXCEPTION: return "I/O exception occurred while accessing database file view";
-        case ZD_E_DB_MUNMAP_MSVIEW: return ZD_E_COULD_NOT("close database file view");
+        case ZD_E_DB_MMAP_MSVIEW  :
+            return ZD_E_COULD_NOT("open database file view");
+        case ZD_E_DB_MAP_EXCEPTION:
+            return "I/O exception occurred while accessing database file view";
+        case ZD_E_DB_MUNMAP_MSVIEW:
+            return ZD_E_COULD_NOT("close database file view");
 #endif
-        case ZD_E_DB_MUNMAP       : return ZD_E_COULD_NOT("unmap database");
-        case ZD_E_DB_CLOSE        : return ZD_E_COULD_NOT("close database file");
-        case ZD_E_PARSE_HEADER    : return ZD_E_COULD_NOT("parse database header");
+        case ZD_E_DB_MUNMAP       :
+            return ZD_E_COULD_NOT("unmap database");
+        case ZD_E_DB_CLOSE        :
+            return ZD_E_COULD_NOT("close database file");
+        case ZD_E_PARSE_HEADER    :
+            return ZD_E_COULD_NOT("parse database header");
     }
 }
 
@@ -736,3 +1125,66 @@ int ZDSetErrorHandler(void (*handler)(int, int))
     zdErrorHandler = handler;
     return 0;
 }
+
+char* ZDHelperSimpleLookupString(const ZoneDetect* library, float lat, float lon)
+{
+    ZoneDetectResult *result = ZDLookup(library, lat, lon, NULL);
+    if(!result) {
+        return NULL;
+    }
+
+    char* output = NULL;
+
+    if(result[0].lookupResult == ZD_LOOKUP_END) {
+        goto done;
+    }
+
+    char* strings[2] = {NULL};
+
+    for(unsigned int i = 0; i < result[0].numFields; i++) {
+        if(result[0].fieldNames[i] && result[0].data[i]) {
+            if(library->tableType == 'T') {
+                if(!strcmp(result[0].fieldNames[i], "TimezoneIdPrefix")) {
+                    strings[0] = result[0].data[i];
+                }
+                if(!strcmp(result[0].fieldNames[i], "TimezoneId")) {
+                    strings[1] = result[0].data[i];
+                }
+            }
+            if(library->tableType == 'C') {
+                if(!strcmp(result[0].fieldNames[i], "Name")) {
+                    strings[0] = result[0].data[i];
+                }
+            }
+        }
+    }
+
+    size_t length = 0;
+    for(unsigned int i=0; i<sizeof(strings)/sizeof(char*); i++) {
+        if(strings[i]) {
+            size_t partLength = strlen(strings[i]);
+            if(partLength > 512) {
+                goto done;
+            }
+            length += partLength;
+        }
+    }
+
+    if(length == 0) {
+        goto done;
+    }
+
+    length += 1;
+
+    output = (char*)malloc(length);
+    output[0] = 0;
+    for(unsigned int i=0; i<sizeof(strings)/sizeof(char*); i++) {
+        if(strings[i]) {
+            strcat(output + strlen(output), strings[i]);
+        }
+    }
+
+done:
+    ZDFreeResults(result);
+    return output;
+}
index d23bbdf..710cb5a 100644 (file)
 #define INCL_ZONEDETECT_H_
 
 #if !defined(ZD_EXPORT)
-  #if defined(_MSC_VER)
-    #define ZD_EXPORT __declspec(dllimport)
-  #else
-    #define ZD_EXPORT
-  #endif
+#if defined(_MSC_VER)
+#define ZD_EXPORT __declspec(dllimport)
+#else
+#define ZD_EXPORT
+#endif
 #endif
 
 typedef enum {
@@ -52,6 +52,7 @@ typedef enum {
 typedef struct {
     ZDLookupResult lookupResult;
 
+    uint32_t polygonId;
     uint32_t metaId;
     uint8_t numFields;
     char **fieldNames;
@@ -66,6 +67,7 @@ extern "C" {
 #endif
 
 ZD_EXPORT ZoneDetect *ZDOpenDatabase(const char *path);
+ZD_EXPORT ZoneDetect *ZDOpenDatabaseFromMemory(void* buffer, size_t length);
 ZD_EXPORT void        ZDCloseDatabase(ZoneDetect *library);
 
 ZD_EXPORT ZoneDetectResult *ZDLookup(const ZoneDetect *library, float lat, float lon, float *safezone);
@@ -78,6 +80,10 @@ ZD_EXPORT const char *ZDLookupResultToString(ZDLookupResult result);
 ZD_EXPORT int         ZDSetErrorHandler(void (*handler)(int, int));
 ZD_EXPORT const char *ZDGetErrorString(int errZD);
 
+ZD_EXPORT float* ZDPolygonToList(const ZoneDetect *library, uint32_t polygonId, size_t* length);
+
+ZD_EXPORT char* ZDHelperSimpleLookupString(const ZoneDetect* library, float lat, float lon);
+
 #ifdef __cplusplus
 }
 #endif