diff options
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/compression.cpp | 81 | ||||
-rw-r--r-- | src/util/filesource.cpp | 58 | ||||
-rw-r--r-- | src/util/parsedate.c | 689 | ||||
-rw-r--r-- | src/util/raster.cpp | 13 | ||||
-rw-r--r-- | src/util/sqlite3.cpp | 166 | ||||
-rw-r--r-- | src/util/uv-channel.c | 69 | ||||
-rw-r--r-- | src/util/uv-messenger.c | 74 | ||||
-rw-r--r-- | src/util/uv-worker.c | 166 | ||||
-rw-r--r-- | src/util/uv.cpp | 12 |
9 files changed, 1263 insertions, 65 deletions
diff --git a/src/util/compression.cpp b/src/util/compression.cpp new file mode 100644 index 0000000000..de4e09764c --- /dev/null +++ b/src/util/compression.cpp @@ -0,0 +1,81 @@ +#include <mbgl/util/compression.hpp> + +#include <zlib.h> + +#include <cstring> +#include <stdexcept> + +namespace mbgl { +namespace util { + +std::string compress(const std::string &raw) { + z_stream deflate_stream; + memset(&deflate_stream, 0, sizeof(deflate_stream)); + + // TODO: reuse z_streams + if (deflateInit(&deflate_stream, Z_DEFAULT_COMPRESSION) != Z_OK) { + throw std::runtime_error("failed to initialize deflate"); + } + + deflate_stream.next_in = (Bytef *)raw.data(); + deflate_stream.avail_in = raw.size(); + + std::string result; + char out[16384]; + + int code; + do { + deflate_stream.next_out = reinterpret_cast<Bytef *>(out); + deflate_stream.avail_out = sizeof(out); + code = deflate(&deflate_stream, Z_FINISH); + if (result.size() < deflate_stream.total_out) { + // append the block to the output string + result.append(out, deflate_stream.total_out - result.size()); + } + } while (code == Z_OK); + + deflateEnd(&deflate_stream); + + if (code != Z_STREAM_END) { + throw std::runtime_error(deflate_stream.msg); + } + + return result; +} + +std::string decompress(const std::string &raw) { + z_stream inflate_stream; + memset(&inflate_stream, 0, sizeof(inflate_stream)); + + // TODO: reuse z_streams + if (inflateInit(&inflate_stream) != Z_OK) { + throw std::runtime_error("failed to initialize inflate"); + } + + inflate_stream.next_in = (Bytef *)raw.data(); + inflate_stream.avail_in = raw.size(); + + std::string result; + char out[15384]; + + int code; + do { + inflate_stream.next_out = reinterpret_cast<Bytef *>(out); + inflate_stream.avail_out = sizeof(out); + code = inflate(&inflate_stream, 0); + // result.append(out, sizeof(out) - inflate_stream.avail_out); + if (result.size() < inflate_stream.total_out) { + result.append(out, inflate_stream.total_out - result.size()); + } + } while (code == Z_OK); + + inflateEnd(&inflate_stream); + + if (code != Z_STREAM_END) { + throw std::runtime_error(inflate_stream.msg); + } + + return result; +} +} +} diff --git a/src/util/filesource.cpp b/src/util/filesource.cpp deleted file mode 100644 index 503d4dfb1d..0000000000 --- a/src/util/filesource.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include <mbgl/util/filesource.hpp> -#include <mbgl/platform/platform.hpp> - -#include <fstream> -#include <sstream> - -namespace mbgl { - -FileSource::FileSource() {} - - -void FileSource::setBase(const std::string &value) { - base = value; -} - -const std::string &FileSource::getBase() const { - return base; -} - -void FileSource::load(ResourceType /*type*/, const std::string &url, std::function<void(platform::Response *)> callback, const std::shared_ptr<uv::loop> loop) { - // convert relative URLs to absolute URLs - - const std::string absoluteURL = [&]() -> std::string { - const size_t separator = url.find("://"); - if (separator == std::string::npos) { - // Relative URL. - return base + url; - } else { - return url; - } - }(); - - const size_t separator = absoluteURL.find("://"); - const std::string protocol = separator != std::string::npos ? absoluteURL.substr(0, separator) : ""; - - if (protocol == "file") { - // load from disk - const std::string path = absoluteURL.substr(separator + 3); - std::ifstream file(path); - - platform::Response response(callback); - if (!file.good()) { - response.error_message = "file not found (" + path + ")"; - } else { - std::stringstream data; - data << file.rdbuf(); - response.code = 200; - response.body = data.str(); - } - - callback(&response); - } else { - // load from the internet - platform::request_http(absoluteURL, callback, loop); - } -} - -}
\ No newline at end of file diff --git a/src/util/parsedate.c b/src/util/parsedate.c new file mode 100644 index 0000000000..123c5c4e5f --- /dev/null +++ b/src/util/parsedate.c @@ -0,0 +1,689 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +/* + A brief summary of the date string formats this parser groks: + + RFC 2616 3.3.1 + + Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + + we support dates without week day name: + + 06 Nov 1994 08:49:37 GMT + 06-Nov-94 08:49:37 GMT + Nov 6 08:49:37 1994 + + without the time zone: + + 06 Nov 1994 08:49:37 + 06-Nov-94 08:49:37 + + weird order: + + 1994 Nov 6 08:49:37 (GNU date fails) + GMT 08:49:37 06-Nov-94 Sunday + 94 6 Nov 08:49:37 (GNU date fails) + + time left out: + + 1994 Nov 6 + 06-Nov-94 + Sun Nov 6 94 + + unusual separators: + + 1994.Nov.6 + Sun/Nov/6/94/GMT + + commonly used time zone names: + + Sun, 06 Nov 1994 08:49:37 CET + 06 Nov 1994 08:49:37 EST + + time zones specified using RFC822 style: + + Sun, 12 Sep 2004 15:05:58 -0700 + Sat, 11 Sep 2004 21:32:11 +0200 + + compact numerical date strings: + + 20040912 15:05:58 -0700 + 20040911 +0200 + +*/ + +#include <mbgl/util/parsedate.h> + + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <limits.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> + + +#define ERRNO (errno) +#define SET_ERRNO(x) (errno = (x)) + + +/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because + its behavior is altered by the current locale. */ +char raw_toupper(char in) +{ + switch (in) { + case 'a': + return 'A'; + case 'b': + return 'B'; + case 'c': + return 'C'; + case 'd': + return 'D'; + case 'e': + return 'E'; + case 'f': + return 'F'; + case 'g': + return 'G'; + case 'h': + return 'H'; + case 'i': + return 'I'; + case 'j': + return 'J'; + case 'k': + return 'K'; + case 'l': + return 'L'; + case 'm': + return 'M'; + case 'n': + return 'N'; + case 'o': + return 'O'; + case 'p': + return 'P'; + case 'q': + return 'Q'; + case 'r': + return 'R'; + case 's': + return 'S'; + case 't': + return 'T'; + case 'u': + return 'U'; + case 'v': + return 'V'; + case 'w': + return 'W'; + case 'x': + return 'X'; + case 'y': + return 'Y'; + case 'z': + return 'Z'; + } + return in; +} + +/* + * raw_equal() is for doing "raw" case insensitive strings. This is meant + * to be locale independent and only compare strings we know are safe for + * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for + * some further explanation to why this function is necessary. + * + * The function is capable of comparing a-z case insensitively even for + * non-ascii. + */ + +int raw_equal(const char *first, const char *second) +{ + while(*first && *second) { + if(raw_toupper(*first) != raw_toupper(*second)) + /* get out of the loop as soon as they don't match */ + break; + first++; + second++; + } + /* we do the comparison here (possibly again), just to make sure that if the + loop above is skipped because one of the strings reached zero, we must not + return this as a successful match */ + return (raw_toupper(*first) == raw_toupper(*second)); +} + +#define ISSPACE(x) (isspace((int) ((unsigned char)x))) +#define ISDIGIT(x) (isdigit((int) ((unsigned char)x))) +#define ISALNUM(x) (isalnum((int) ((unsigned char)x))) +#define ISALPHA(x) (isalpha((int) ((unsigned char)x))) + + +/* + * Redefine TRUE and FALSE too, to catch current use. With this + * change, 'bool found = 1' will give a warning on MIPSPro, but + * 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro, + * AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too. + */ + +#ifndef TRUE +#define TRUE true +#endif +#ifndef FALSE +#define FALSE false +#endif + + + +/* +** signed long to signed int +*/ + +int clamp_to_int(long slnum) +{ + return slnum > INT_MAX ? INT_MAX : slnum < INT_MIN ? INT_MIN : slnum; +} + + +const char * const wkday[] = +{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; +static const char * const weekday[] = +{ "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday" }; +const char * const month[]= +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +struct tzinfo { + char name[5]; + int offset; /* +/- in minutes */ +}; + +/* + * parsedate() + * + * Returns: + * + * PARSEDATE_OK - a fine conversion + * PARSEDATE_FAIL - failed to convert + * PARSEDATE_LATER - time overflow at the far end of time_t + * PARSEDATE_SOONER - time underflow at the low end of time_t + */ + +static int parsedate(const char *date, time_t *output); + +#define PARSEDATE_OK 0 +#define PARSEDATE_FAIL -1 +#define PARSEDATE_LATER 1 +#define PARSEDATE_SOONER 2 + +/* Here's a bunch of frequently used time zone names. These were supported + by the old getdate parser. */ +#define tDAYZONE -60 /* offset for daylight savings time */ +static const struct tzinfo tz[]= { + {"GMT", 0}, /* Greenwich Mean */ + {"UTC", 0}, /* Universal (Coordinated) */ + {"WET", 0}, /* Western European */ + {"BST", 0 tDAYZONE}, /* British Summer */ + {"WAT", 60}, /* West Africa */ + {"AST", 240}, /* Atlantic Standard */ + {"ADT", 240 tDAYZONE}, /* Atlantic Daylight */ + {"EST", 300}, /* Eastern Standard */ + {"EDT", 300 tDAYZONE}, /* Eastern Daylight */ + {"CST", 360}, /* Central Standard */ + {"CDT", 360 tDAYZONE}, /* Central Daylight */ + {"MST", 420}, /* Mountain Standard */ + {"MDT", 420 tDAYZONE}, /* Mountain Daylight */ + {"PST", 480}, /* Pacific Standard */ + {"PDT", 480 tDAYZONE}, /* Pacific Daylight */ + {"YST", 540}, /* Yukon Standard */ + {"YDT", 540 tDAYZONE}, /* Yukon Daylight */ + {"HST", 600}, /* Hawaii Standard */ + {"HDT", 600 tDAYZONE}, /* Hawaii Daylight */ + {"CAT", 600}, /* Central Alaska */ + {"AHST", 600}, /* Alaska-Hawaii Standard */ + {"NT", 660}, /* Nome */ + {"IDLW", 720}, /* International Date Line West */ + {"CET", -60}, /* Central European */ + {"MET", -60}, /* Middle European */ + {"MEWT", -60}, /* Middle European Winter */ + {"MEST", -60 tDAYZONE}, /* Middle European Summer */ + {"CEST", -60 tDAYZONE}, /* Central European Summer */ + {"MESZ", -60 tDAYZONE}, /* Middle European Summer */ + {"FWT", -60}, /* French Winter */ + {"FST", -60 tDAYZONE}, /* French Summer */ + {"EET", -120}, /* Eastern Europe, USSR Zone 1 */ + {"WAST", -420}, /* West Australian Standard */ + {"WADT", -420 tDAYZONE}, /* West Australian Daylight */ + {"CCT", -480}, /* China Coast, USSR Zone 7 */ + {"JST", -540}, /* Japan Standard, USSR Zone 8 */ + {"EAST", -600}, /* Eastern Australian Standard */ + {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */ + {"GST", -600}, /* Guam Standard, USSR Zone 9 */ + {"NZT", -720}, /* New Zealand */ + {"NZST", -720}, /* New Zealand Standard */ + {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */ + {"IDLE", -720}, /* International Date Line East */ + /* Next up: Military timezone names. RFC822 allowed these, but (as noted in + RFC 1123) had their signs wrong. Here we use the correct signs to match + actual military usage. + */ + {"A", +1 * 60}, /* Alpha */ + {"B", +2 * 60}, /* Bravo */ + {"C", +3 * 60}, /* Charlie */ + {"D", +4 * 60}, /* Delta */ + {"E", +5 * 60}, /* Echo */ + {"F", +6 * 60}, /* Foxtrot */ + {"G", +7 * 60}, /* Golf */ + {"H", +8 * 60}, /* Hotel */ + {"I", +9 * 60}, /* India */ + /* "J", Juliet is not used as a timezone, to indicate the observer's local + time */ + {"K", +10 * 60}, /* Kilo */ + {"L", +11 * 60}, /* Lima */ + {"M", +12 * 60}, /* Mike */ + {"N", -1 * 60}, /* November */ + {"O", -2 * 60}, /* Oscar */ + {"P", -3 * 60}, /* Papa */ + {"Q", -4 * 60}, /* Quebec */ + {"R", -5 * 60}, /* Romeo */ + {"S", -6 * 60}, /* Sierra */ + {"T", -7 * 60}, /* Tango */ + {"U", -8 * 60}, /* Uniform */ + {"V", -9 * 60}, /* Victor */ + {"W", -10 * 60}, /* Whiskey */ + {"X", -11 * 60}, /* X-ray */ + {"Y", -12 * 60}, /* Yankee */ + {"Z", 0}, /* Zulu, zero meridian, a.k.a. UTC */ +}; + +/* returns: + -1 no day + 0 monday - 6 sunday +*/ + +static int checkday(const char *check, size_t len) +{ + int i; + const char * const *what; + bool found= FALSE; + if(len > 3) + what = &weekday[0]; + else + what = &wkday[0]; + for(i=0; i<7; i++) { + if(raw_equal(check, what[0])) { + found=TRUE; + break; + } + what++; + } + return found?i:-1; +} + +static int checkmonth(const char *check) +{ + int i; + const char * const *what; + bool found= FALSE; + + what = &month[0]; + for(i=0; i<12; i++) { + if(raw_equal(check, what[0])) { + found=TRUE; + break; + } + what++; + } + return found?i:-1; /* return the offset or -1, no real offset is -1 */ +} + +/* return the time zone offset between GMT and the input one, in number + of seconds or -1 if the timezone wasn't found/legal */ + +static int checktz(const char *check) +{ + unsigned int i; + const struct tzinfo *what; + bool found= FALSE; + + what = tz; + for(i=0; i< sizeof(tz)/sizeof(tz[0]); i++) { + if(raw_equal(check, what->name)) { + found=TRUE; + break; + } + what++; + } + return found?what->offset*60:-1; +} + +static void skip(const char **date) +{ + /* skip everything that aren't letters or digits */ + while(**date && !ISALNUM(**date)) + (*date)++; +} + +enum assume { + DATE_MDAY, + DATE_YEAR, + DATE_TIME +}; + +/* this is a clone of 'struct tm' but with all fields we don't need or use + cut out */ +struct my_tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; +}; + +/* struct tm to time since epoch in GMT time zone. + * This is similar to the standard mktime function but for GMT only, and + * doesn't suffer from the various bugs and portability problems that + * some systems' implementations have. + */ +static time_t my_timegm(struct my_tm *tm) +{ + static const int month_days_cumulative [12] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int month, year, leap_days; + + if(tm->tm_year < 70) + /* we don't support years before 1970 as they will cause this function + to return a negative value */ + return -1; + + year = tm->tm_year + 1900; + month = tm->tm_mon; + if(month < 0) { + year += (11 - month) / 12; + month = 11 - (11 - month) % 12; + } + else if(month >= 12) { + year -= month / 12; + month = month % 12; + } + + leap_days = year - (tm->tm_mon <= 1); + leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400) + - (1969 / 4) + (1969 / 100) - (1969 / 400)); + + return ((((time_t) (year - 1970) * 365 + + leap_days + month_days_cumulative [month] + tm->tm_mday - 1) * 24 + + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec; +} + +/* + * parsedate() + * + * Returns: + * + * PARSEDATE_OK - a fine conversion + * PARSEDATE_FAIL - failed to convert + * PARSEDATE_LATER - time overflow at the far end of time_t + * PARSEDATE_SOONER - time underflow at the low end of time_t + */ + +static int parsedate(const char *date, time_t *output) +{ + time_t t = 0; + int wdaynum=-1; /* day of the week number, 0-6 (mon-sun) */ + int monnum=-1; /* month of the year number, 0-11 */ + int mdaynum=-1; /* day of month, 1 - 31 */ + int hournum=-1; + int minnum=-1; + int secnum=-1; + int yearnum=-1; + int tzoff=-1; + struct my_tm tm; + enum assume dignext = DATE_MDAY; + const char *indate = date; /* save the original pointer */ + int part = 0; /* max 6 parts */ + + while(*date && (part < 6)) { + bool found=FALSE; + + skip(&date); + + if(ISALPHA(*date)) { + /* a name coming up */ + char buf[32]=""; + size_t len; + if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz]", buf)) + len = strlen(buf); + else + len = 0; + + if(wdaynum == -1) { + wdaynum = checkday(buf, len); + if(wdaynum != -1) + found = TRUE; + } + if(!found && (monnum == -1)) { + monnum = checkmonth(buf); + if(monnum != -1) + found = TRUE; + } + + if(!found && (tzoff == -1)) { + /* this just must be a time zone string */ + tzoff = checktz(buf); + if(tzoff != -1) + found = TRUE; + } + + if(!found) + return PARSEDATE_FAIL; /* bad string */ + + date += len; + } + else if(ISDIGIT(*date)) { + /* a digit */ + int val; + char *end; + if((secnum == -1) && + (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) { + /* time stamp! */ + date += 8; + } + else if((secnum == -1) && + (2 == sscanf(date, "%02d:%02d", &hournum, &minnum))) { + /* time stamp without seconds */ + date += 5; + secnum = 0; + } + else { + long lval; + int error; + int old_errno; + + old_errno = ERRNO; + SET_ERRNO(0); + lval = strtol(date, &end, 10); + error = ERRNO; + if(error != old_errno) + SET_ERRNO(old_errno); + + if(error) + return PARSEDATE_FAIL; + +#if LONG_MAX != INT_MAX + if((lval > (long)INT_MAX) || (lval < (long)INT_MIN)) + return PARSEDATE_FAIL; +#endif + + val = clamp_to_int(lval); + + if((tzoff == -1) && + ((end - date) == 4) && + (val <= 1400) && + (indate< date) && + ((date[-1] == '+' || date[-1] == '-'))) { + /* four digits and a value less than or equal to 1400 (to take into + account all sorts of funny time zone diffs) and it is preceded + with a plus or minus. This is a time zone indication. 1400 is + picked since +1300 is frequently used and +1400 is mentioned as + an edge number in the document "ISO C 200X Proposal: Timezone + Functions" at http://david.tribble.com/text/c0xtimezone.html If + anyone has a more authoritative source for the exact maximum time + zone offsets, please speak up! */ + found = TRUE; + tzoff = (val/100 * 60 + val%100)*60; + + /* the + and - prefix indicates the local time compared to GMT, + this we need ther reversed math to get what we want */ + tzoff = date[-1]=='+'?-tzoff:tzoff; + } + + if(((end - date) == 8) && + (yearnum == -1) && + (monnum == -1) && + (mdaynum == -1)) { + /* 8 digits, no year, month or day yet. This is YYYYMMDD */ + found = TRUE; + yearnum = val/10000; + monnum = (val%10000)/100-1; /* month is 0 - 11 */ + mdaynum = val%100; + } + + if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { + if((val > 0) && (val<32)) { + mdaynum = val; + found = TRUE; + } + dignext = DATE_YEAR; + } + + if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { + yearnum = val; + found = TRUE; + if(yearnum < 1900) { + if(yearnum > 70) + yearnum += 1900; + else + yearnum += 2000; + } + if(mdaynum == -1) + dignext = DATE_MDAY; + } + + if(!found) + return PARSEDATE_FAIL; + + date = end; + } + } + + part++; + } + + if(-1 == secnum) + secnum = minnum = hournum = 0; /* no time, make it zero */ + + if((-1 == mdaynum) || + (-1 == monnum) || + (-1 == yearnum)) + /* lacks vital info, fail */ + return PARSEDATE_FAIL; + +#if SIZEOF_TIME_T < 5 + /* 32 bit time_t can only hold dates to the beginning of 2038 */ + if(yearnum > 2037) { + *output = 0x7fffffff; + return PARSEDATE_LATER; + } +#endif + + if(yearnum < 1970) { + *output = 0; + return PARSEDATE_SOONER; + } + + if((mdaynum > 31) || (monnum > 11) || + (hournum > 23) || (minnum > 59) || (secnum > 60)) + return PARSEDATE_FAIL; /* clearly an illegal date */ + + tm.tm_sec = secnum; + tm.tm_min = minnum; + tm.tm_hour = hournum; + tm.tm_mday = mdaynum; + tm.tm_mon = monnum; + tm.tm_year = yearnum - 1900; + + /* my_timegm() returns a time_t. time_t is often 32 bits, even on many + architectures that feature 64 bit 'long'. + + Some systems have 64 bit time_t and deal with years beyond 2038. However, + even on some of the systems with 64 bit time_t mktime() returns -1 for + dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06) + */ + t = my_timegm(&tm); + + /* time zone adjust (cast t to int to compare to negative one) */ + if(-1 != (int)t) { + + /* Add the time zone diff between local time zone and GMT. */ + long delta = (long)(tzoff!=-1?tzoff:0); + + if((delta>0) && (t > LONG_MAX - delta)) + return -1; /* time_t overflow */ + + t += delta; + } + + *output = t; + + return PARSEDATE_OK; +} + +time_t parse_date(const char *p) +{ + time_t parsed; + int rc = parsedate(p, &parsed); + + switch(rc) { + case PARSEDATE_OK: + case PARSEDATE_LATER: + case PARSEDATE_SOONER: + return parsed; + } + /* everything else is fail */ + return -1; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/util/raster.cpp b/src/util/raster.cpp index 9a71573d01..7b52c51037 100644 --- a/src/util/raster.cpp +++ b/src/util/raster.cpp @@ -1,20 +1,19 @@ -#include <mbgl/util/raster.hpp> - -#include <memory> -#include <cassert> -#include <cstring> - #include <mbgl/platform/platform.hpp> #include <mbgl/platform/gl.hpp> + +#include <mbgl/util/raster.hpp> #include <mbgl/util/time.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/util/std.hpp> #include <png.h> +#include <cassert> +#include <cstring> + using namespace mbgl; -Raster::Raster(const std::shared_ptr<Texturepool> &texturepool) +Raster::Raster(const util::ptr<Texturepool> &texturepool) : texturepool(texturepool) {} diff --git a/src/util/sqlite3.cpp b/src/util/sqlite3.cpp new file mode 100644 index 0000000000..19e0ce9c79 --- /dev/null +++ b/src/util/sqlite3.cpp @@ -0,0 +1,166 @@ +#include <mbgl/util/sqlite3.hpp> +#include <sqlite3.h> + +#include <cassert> + +namespace mapbox { +namespace sqlite { + +Database::Database(const std::string &filename, int flags) { + const int err = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr); + if (err != SQLITE_OK) { + Exception ex { err, sqlite3_errmsg(db) }; + db = nullptr; + throw ex; + } +} + +Database::Database(Database &&other) { + *this = std::move(other); +} + +Database &Database::operator=(Database &&other) { + std::swap(db, other.db); + return *this; +} + +Database::~Database() { + if (db) { + const int err = sqlite3_close(db); + if (err != SQLITE_OK) { + throw Exception { err, sqlite3_errmsg(db) }; + } + } +} + +Database::operator bool() const { + return db != nullptr; +} + +void Database::exec(const std::string &sql) { + assert(db); + char *msg = nullptr; + const int err = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &msg); + if (msg) { + Exception ex { err, msg }; + sqlite3_free(msg); + throw ex; + } else if (err != SQLITE_OK) { + throw Exception { err, sqlite3_errmsg(db) }; + } +} + +Statement Database::prepare(const char *query) { + assert(db); + return std::move(Statement(db, query)); +} + +Statement::Statement(sqlite3 *db, const char *sql) { + const int err = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); + if (err != SQLITE_OK) { + stmt = nullptr; + throw Exception { err, sqlite3_errmsg(db) }; + } +} + +#define CHECK_SQLITE_OK(err) \ + if (err != SQLITE_OK) { \ + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; \ + } + +Statement::Statement(Statement &&other) { + *this = std::move(other); +} + +Statement &Statement::operator=(Statement &&other) { + std::swap(stmt, other.stmt); + return *this; +} + +Statement::~Statement() { + if (stmt) { + const int err = sqlite3_finalize(stmt); + CHECK_SQLITE_OK(err) + } +} + +Statement::operator bool() const { + return stmt != nullptr; +} + +#define BIND_3(type, value) \ + assert(stmt); \ + const int err = sqlite3_bind_##type(stmt, offset, value); \ + CHECK_SQLITE_OK(err) + +#define BIND_5(type, value, length, param) \ + assert(stmt); \ + const int err = sqlite3_bind_##type(stmt, offset, value, length, param); \ + CHECK_SQLITE_OK(err) + +template <> void Statement::bind(int offset, int value) { + BIND_3(int, value) +} + +template <> void Statement::bind(int offset, int64_t value) { + BIND_3(int64, value) +} + +template <> void Statement::bind(int offset, double value) { + BIND_3(double, value) +} + +template <> void Statement::bind(int offset, bool value) { + BIND_3(int, value) +} + +template <> void Statement::bind(int offset, const char *value) { + BIND_5(text, value, -1, nullptr) +} + +void Statement::bind(int offset, const std::string &value, bool retain) { + BIND_5(blob, value.data(), int(value.size()), retain ? SQLITE_TRANSIENT : SQLITE_STATIC) +} + +bool Statement::run() { + assert(stmt); + const int err = sqlite3_step(stmt); + if (err == SQLITE_DONE) { + return false; + } else if (err == SQLITE_ROW) { + return true; + } else { + throw std::runtime_error("failed to run statement"); + } +} + +template <> int Statement::get(int offset) { + assert(stmt); + return sqlite3_column_int(stmt, offset); +} + +template <> int64_t Statement::get(int offset) { + assert(stmt); + return sqlite3_column_int64(stmt, offset); +} + +template <> double Statement::get(int offset) { + assert(stmt); + return sqlite3_column_double(stmt, offset); +} + +template <> std::string Statement::get(int offset) { + assert(stmt); + return { + reinterpret_cast<const char *>(sqlite3_column_blob(stmt, offset)), + size_t(sqlite3_column_bytes(stmt, offset)) + }; +} + +void Statement::reset() { + assert(stmt); + sqlite3_reset(stmt); +} + +} +} diff --git a/src/util/uv-channel.c b/src/util/uv-channel.c new file mode 100644 index 0000000000..4e3b9fa5ff --- /dev/null +++ b/src/util/uv-channel.c @@ -0,0 +1,69 @@ +#include <mbgl/util/uv-channel.h> +#include <mbgl/util/queue.h> + +#include <stdlib.h> + +// Taken from http://navaneeth.github.io/blog/2013/08/02/channels-in-libuv/ + +typedef struct { + void *data; + void *active_queue[2]; +} uv__chan_item_t; + +int uv_chan_init(uv_chan_t *chan) { + int r = uv_mutex_init(&chan->mutex); + if (r == -1) + return r; + + QUEUE_INIT(&chan->q); + + return uv_cond_init(&chan->cond); +} + +void uv_chan_send(uv_chan_t *chan, void *data) { + uv__chan_item_t *item = (uv__chan_item_t *)malloc(sizeof(uv__chan_item_t)); + item->data = data; + + uv_mutex_lock(&chan->mutex); + QUEUE_INSERT_TAIL(&chan->q, &item->active_queue); + uv_cond_signal(&chan->cond); + uv_mutex_unlock(&chan->mutex); +} + +void *uv_chan_receive(uv_chan_t *chan) { + uv__chan_item_t *item; + QUEUE *head; + void *data = NULL; + + uv_mutex_lock(&chan->mutex); + while (QUEUE_EMPTY(&chan->q)) { + uv_cond_wait(&chan->cond, &chan->mutex); + } + + head = QUEUE_HEAD(&chan->q); + item = QUEUE_DATA(head, uv__chan_item_t, active_queue); + data = item->data; + QUEUE_REMOVE(head); + free(item); + uv_mutex_unlock(&chan->mutex); + return data; +} + +void uv_chan_clear(uv_chan_t *chan) { + uv_mutex_lock(&chan->mutex); + uv__chan_item_t *item = NULL; + QUEUE *head = NULL; + while (!QUEUE_EMPTY(&chan->q)) { + head = QUEUE_HEAD(&chan->q); + item = QUEUE_DATA(head, uv__chan_item_t, active_queue); + QUEUE_REMOVE(head); + free(item); + } + uv_mutex_unlock(&chan->mutex); +} + +void uv_chan_destroy(uv_chan_t *chan) { + uv_chan_clear(chan); + uv_cond_destroy(&chan->cond); + uv_mutex_destroy(&chan->mutex); +} diff --git a/src/util/uv-messenger.c b/src/util/uv-messenger.c new file mode 100644 index 0000000000..a25c84dc59 --- /dev/null +++ b/src/util/uv-messenger.c @@ -0,0 +1,74 @@ +#include <mbgl/util/uv-messenger.h> +#include <mbgl/util/queue.h> + +#include <stdlib.h> + +typedef struct { + void *data; + void *queue[2]; +} uv__messenger_item_t; + +void uv__messenger_callback(uv_async_t *async) { + uv_messenger_t *msgr = (uv_messenger_t *)async->data; + + uv__messenger_item_t *item; + QUEUE *head; + + while (1) { + uv_mutex_lock(&msgr->mutex); + if (QUEUE_EMPTY(&msgr->queue)) { + uv_mutex_unlock(&msgr->mutex); + break; + } + + head = QUEUE_HEAD(&msgr->queue); + item = QUEUE_DATA(head, uv__messenger_item_t, queue); + QUEUE_REMOVE(head); + uv_mutex_unlock(&msgr->mutex); + + msgr->callback(item->data); + + free(item); + } +} + +int uv_messenger_init(uv_loop_t *loop, uv_messenger_t *msgr, uv_messenger_cb callback) { + int ret = uv_mutex_init(&msgr->mutex); + if (ret < 0) { + return ret; + } + + msgr->callback = callback; + + QUEUE_INIT(&msgr->queue); + + msgr->async.data = msgr; + return uv_async_init(loop, &msgr->async, uv__messenger_callback); +} + +void uv_messenger_send(uv_messenger_t *msgr, void *data) { + uv__messenger_item_t *item = (uv__messenger_item_t *)malloc(sizeof(uv__messenger_item_t)); + item->data = data; + + uv_mutex_lock(&msgr->mutex); + QUEUE_INSERT_TAIL(&msgr->queue, &item->queue); + uv_mutex_unlock(&msgr->mutex); + + uv_async_send(&msgr->async); +} + +void uv_messenger_ref(uv_messenger_t *msgr) { + uv_ref((uv_handle_t *)&msgr->async); +} + +void uv_messenger_unref(uv_messenger_t *msgr) { + uv_unref((uv_handle_t *)&msgr->async); +} + +void uv__messenger_stop_callback(uv_handle_t *handle) { + free((uv_messenger_t *)handle->data); +} + +void uv_messenger_stop(uv_messenger_t *msgr) { + uv_close((uv_handle_t *)&msgr->async, uv__messenger_stop_callback); +} diff --git a/src/util/uv-worker.c b/src/util/uv-worker.c new file mode 100644 index 0000000000..8b0cc6dda7 --- /dev/null +++ b/src/util/uv-worker.c @@ -0,0 +1,166 @@ +#include <mbgl/util/uv-worker.h> +#include <mbgl/util/uv-messenger.h> + +#include <stdio.h> +#include <assert.h> + +typedef struct uv__worker_item_s uv__worker_item_t; +struct uv__worker_item_s { + uv_worker_t *worker; + void *data; + uv_worker_cb work_cb; + uv_worker_after_cb after_work_cb; +}; + +typedef struct uv__worker_thread_s uv__worker_thread_t; +struct uv__worker_thread_s { + uv_worker_t *worker; + uv_thread_t thread; +}; + +void uv__worker_thread_finished(uv__worker_thread_t *worker_thread) { + uv_worker_t *worker = worker_thread->worker; + +#ifndef NDEBUG + assert(uv_thread_self() == worker->thread_id); +#endif + + // This should at most block very briefly. We are sending the termination + // notification as the last thing in the worker thread, so by now the thread + // has probably terminated already. If not, the waiting time should be + // extremely short. + uv_thread_join(&worker_thread->thread); + + assert(worker->count > 0); + worker->count--; + if (worker->count == 0) { + uv_chan_destroy(&worker->chan); + uv_messenger_stop(worker->msgr); + if (worker->close_cb) { + worker->close_cb(worker); + } + } +} + +void uv__worker_after(void *ptr) { + uv__worker_item_t *item = (uv__worker_item_t *)ptr; + + if (item->work_cb) { + // We are finishing a regular work request. + if (item->after_work_cb) { + assert(item->after_work_cb); + item->after_work_cb(item->data); + } + uv_worker_t *worker = item->worker; + assert(worker->active_items > 0); + if (--worker->active_items == 0) { + uv_messenger_unref(worker->msgr); + } + } else { + // This is a worker thread termination. + uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)item->data; + uv__worker_thread_finished(worker_thread); + free(worker_thread); + } + + free(item); +} + +void uv__worker_thread_loop(void *ptr) { + uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)ptr; + uv_worker_t *worker = worker_thread->worker; + +#ifdef __APPLE__ + if (worker->name) { + pthread_setname_np(worker->name); + } +#endif + + uv__worker_item_t *item = NULL; + while ((item = (uv__worker_item_t *)uv_chan_receive(&worker->chan)) != NULL) { + assert(item->work_cb); + item->work_cb(item->data); + + // Trigger the after callback in the main thread. + uv_messenger_send(worker->msgr, item); + } + + // Make sure to close all other workers too. + uv_chan_send(&worker->chan, NULL); + + // Create a new worker item that acts as a terminate flag for this thread. + item = (uv__worker_item_t *)malloc(sizeof(uv__worker_item_t)); + item->data = worker_thread; + item->work_cb = NULL; + item->after_work_cb = NULL; + uv_messenger_send(worker->msgr, item); +} + +int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name) { +#ifndef NDEBUG + worker->thread_id = uv_thread_self(); +#endif + worker->loop = loop; + worker->name = name; + worker->count = 0; + worker->close_cb = NULL; + worker->active_items = 0; + worker->msgr = (uv_messenger_t *)malloc(sizeof(uv_messenger_t)); + int ret = uv_messenger_init(loop, worker->msgr, uv__worker_after); + if (ret < 0) { + free(worker->msgr); + return ret; + } + uv_messenger_unref(worker->msgr); + ret = uv_chan_init(&worker->chan); + if (ret < 0) return ret; + + // Initialize all worker threads. + int i; + for (i = 0; i < count; i++) { + uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)malloc(sizeof(uv__worker_thread_t)); + worker_thread->worker = worker; + ret = uv_thread_create(&worker_thread->thread, uv__worker_thread_loop, worker_thread); + if (ret < 0) return ret; + worker->count++; + } + + return 0; +} + +void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb, + uv_worker_after_cb after_work_cb) { +#ifndef NDEBUG + assert(uv_thread_self() == worker->thread_id); +#endif + + // It doesn't make sense to not provide a work callback. On the other hand, the after_work_cb + // may be NULL. In that case, there will be no callback called in the current thread and the + // worker item will instead be freed in the worker thread. + assert(work_cb); + + uv__worker_item_t *item = (uv__worker_item_t *)malloc(sizeof(uv__worker_item_t)); + item->worker = worker; + item->work_cb = work_cb; + item->after_work_cb = after_work_cb; + item->data = data; + uv_chan_send(&worker->chan, item); + if (worker->active_items++ == 0) { + uv_messenger_ref(worker->msgr); + } +} + +void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb) { +#ifndef NDEBUG + assert(uv_thread_self() == worker->thread_id); +#endif + + // Prevent double calling. + assert(worker->close_cb == NULL); + + worker->close_cb = close_cb; + uv_chan_send(&worker->chan, NULL); + if (worker->active_items++ == 0) { + uv_messenger_ref(worker->msgr); + } +} diff --git a/src/util/uv.cpp b/src/util/uv.cpp index 94f074bfa1..6e15ac4537 100644 --- a/src/util/uv.cpp +++ b/src/util/uv.cpp @@ -16,4 +16,16 @@ std::string cwd() { return dir; } +void deleter::operator()(uv_async_t *async) { + uv_close((uv_handle_t *)async, [](uv_handle_t *handle) { + delete (uv_async_t *)handle; + }); +} + +void deleter::operator()(uv_timer_t *timer) { + uv_close((uv_handle_t *)timer, [](uv_handle_t *handle) { + delete (uv_timer_t *)handle; + }); +} + } |