diff options
Diffstat (limited to 'platform/default/src/mbgl/util')
-rw-r--r-- | platform/default/src/mbgl/util/async_task.cpp | 66 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/default_thread_pool.cpp | 57 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/image.cpp | 31 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/jpeg_reader.cpp | 151 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/logging_stderr.cpp | 12 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/png_reader.cpp | 142 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/png_writer.cpp | 77 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/run_loop.cpp | 219 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/shared_thread_pool.cpp | 14 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/string_stdlib.cpp | 74 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/thread.cpp | 37 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/thread_local.cpp | 66 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/timer.cpp | 73 | ||||
-rw-r--r-- | platform/default/src/mbgl/util/utf.cpp | 17 |
14 files changed, 1036 insertions, 0 deletions
diff --git a/platform/default/src/mbgl/util/async_task.cpp b/platform/default/src/mbgl/util/async_task.cpp new file mode 100644 index 0000000000..50891056d8 --- /dev/null +++ b/platform/default/src/mbgl/util/async_task.cpp @@ -0,0 +1,66 @@ +#include <mbgl/util/async_task.hpp> + +#include <mbgl/util/run_loop.hpp> + +#include <atomic> +#include <functional> + +#include <uv.h> + +namespace mbgl { +namespace util { + +class AsyncTask::Impl { +public: + Impl(std::function<void()>&& fn) + : async(new uv_async_t), + task(std::move(fn)) { + + auto* loop = reinterpret_cast<uv_loop_t*>(RunLoop::getLoopHandle()); + if (uv_async_init(loop, async, asyncCallback) != 0) { + throw std::runtime_error("Failed to initialize async."); + } + + handle()->data = this; + uv_unref(handle()); + } + + ~Impl() { + uv_close(handle(), [](uv_handle_t* h) { + delete reinterpret_cast<uv_async_t*>(h); + }); + } + + void maySend() { + // uv_async_send will do the call coalescing for us. + if (uv_async_send(async) != 0) { + throw std::runtime_error("Failed to async send."); + } + } + +private: + static void asyncCallback(uv_async_t* handle) { + reinterpret_cast<Impl*>(handle->data)->task(); + } + + uv_handle_t* handle() { + return reinterpret_cast<uv_handle_t*>(async); + } + + uv_async_t* async; + + std::function<void()> task; +}; + +AsyncTask::AsyncTask(std::function<void()>&& fn) + : impl(std::make_unique<Impl>(std::move(fn))) { +} + +AsyncTask::~AsyncTask() = default; + +void AsyncTask::send() { + impl->maySend(); +} + +} // namespace util +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/default_thread_pool.cpp b/platform/default/src/mbgl/util/default_thread_pool.cpp new file mode 100644 index 0000000000..d3950bb8aa --- /dev/null +++ b/platform/default/src/mbgl/util/default_thread_pool.cpp @@ -0,0 +1,57 @@ +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/util/platform.hpp> +#include <mbgl/util/string.hpp> + +namespace mbgl { + +ThreadPool::ThreadPool(std::size_t count) { + threads.reserve(count); + for (std::size_t i = 0; i < count; ++i) { + threads.emplace_back([this, i]() { + platform::setCurrentThreadName(std::string{ "Worker " } + util::toString(i + 1)); + + while (true) { + std::unique_lock<std::mutex> lock(mutex); + + cv.wait(lock, [this] { + return !queue.empty() || terminate; + }); + + if (terminate) { + return; + } + + auto mailbox = queue.front(); + queue.pop(); + lock.unlock(); + + Mailbox::maybeReceive(mailbox); + } + }); + } +} + +ThreadPool::~ThreadPool() { + { + std::lock_guard<std::mutex> lock(mutex); + terminate = true; + } + + cv.notify_all(); + + for (auto& thread : threads) { + thread.join(); + } +} + +void ThreadPool::schedule(std::weak_ptr<Mailbox> mailbox) { + { + std::lock_guard<std::mutex> lock(mutex); + queue.push(mailbox); + } + + cv.notify_one(); +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/image.cpp b/platform/default/src/mbgl/util/image.cpp new file mode 100644 index 0000000000..25063892b7 --- /dev/null +++ b/platform/default/src/mbgl/util/image.cpp @@ -0,0 +1,31 @@ +#include <mbgl/util/image.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/premultiply.hpp> + +namespace mbgl { + +PremultipliedImage decodePNG(const uint8_t*, size_t); +PremultipliedImage decodeJPEG(const uint8_t*, size_t); + +PremultipliedImage decodeImage(const std::string& string) { + const auto* data = reinterpret_cast<const uint8_t*>(string.data()); + const size_t size = string.size(); + + if (size >= 4) { + uint32_t magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + if (magic == 0x89504E47U) { + return decodePNG(data, size); + } + } + + if (size >= 2) { + uint16_t magic = ((data[0] << 8) | data[1]) & 0xffff; + if (magic == 0xFFD8) { + return decodeJPEG(data, size); + } + } + + throw std::runtime_error("unsupported image type"); +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/jpeg_reader.cpp b/platform/default/src/mbgl/util/jpeg_reader.cpp new file mode 100644 index 0000000000..5f613f9423 --- /dev/null +++ b/platform/default/src/mbgl/util/jpeg_reader.cpp @@ -0,0 +1,151 @@ +#include <mbgl/util/image.hpp> +#include <mbgl/util/char_array_buffer.hpp> + +#include <istream> +#include <sstream> +#include <array> + +extern "C" +{ +#include <jpeglib.h> +} + +namespace mbgl { + +const static unsigned BUF_SIZE = 4096; + +struct jpeg_stream_wrapper { + jpeg_source_mgr manager; + std::istream* stream; + std::array<JOCTET, BUF_SIZE> buffer; +}; + +static void init_source(j_decompress_ptr cinfo) { + auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src); + wrap->stream->seekg(0, std::ios_base::beg); +} + +static boolean fill_input_buffer(j_decompress_ptr cinfo) { + auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src); + wrap->stream->read(reinterpret_cast<char*>(&wrap->buffer[0]), BUF_SIZE); + std::streamsize size = wrap->stream->gcount(); + wrap->manager.next_input_byte = wrap->buffer.data(); + wrap->manager.bytes_in_buffer = BUF_SIZE; + return (size > 0) ? TRUE : FALSE; +} + +static void skip(j_decompress_ptr cinfo, long count) { + if (count <= 0) return; // A zero or negative skip count should be treated as a no-op. + auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src); + + if (wrap->manager.bytes_in_buffer > 0 && count < static_cast<long>(wrap->manager.bytes_in_buffer)) + { + wrap->manager.bytes_in_buffer -= count; + wrap->manager.next_input_byte = &wrap->buffer[BUF_SIZE - wrap->manager.bytes_in_buffer]; + } + else + { + wrap->stream->seekg(count - wrap->manager.bytes_in_buffer, std::ios_base::cur); + // trigger buffer fill + wrap->manager.next_input_byte = nullptr; + wrap->manager.bytes_in_buffer = 0; // bytes_in_buffer may be zero on return. + } +} + +static void term(j_decompress_ptr) {} + +static void attach_stream(j_decompress_ptr cinfo, std::istream* in) { + if (cinfo->src == nullptr) { + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(jpeg_stream_wrapper)); + } + auto * src = reinterpret_cast<jpeg_stream_wrapper*> (cinfo->src); + src->manager.init_source = init_source; + src->manager.fill_input_buffer = fill_input_buffer; + src->manager.skip_input_data = skip; + src->manager.resync_to_restart = jpeg_resync_to_restart; + src->manager.term_source = term; + src->manager.bytes_in_buffer = 0; + src->manager.next_input_byte = nullptr; + src->stream = in; +} + +static void on_error(j_common_ptr) {} + +static void on_error_message(j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + throw std::runtime_error(std::string("JPEG Reader: libjpeg could not read image: ") + buffer); +} + +struct jpeg_info_guard { + jpeg_info_guard(jpeg_decompress_struct* cinfo) + : i_(cinfo) {} + + ~jpeg_info_guard() { + jpeg_destroy_decompress(i_); + } + + jpeg_decompress_struct* i_; +}; + +PremultipliedImage decodeJPEG(const uint8_t* data, size_t size) { + util::CharArrayBuffer dataBuffer { reinterpret_cast<const char*>(data), size }; + std::istream stream(&dataBuffer); + + jpeg_decompress_struct cinfo; + jpeg_info_guard iguard(&cinfo); + jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = on_error; + jerr.output_message = on_error_message; + jpeg_create_decompress(&cinfo); + attach_stream(&cinfo, &stream); + + int ret = jpeg_read_header(&cinfo, TRUE); + if (ret != JPEG_HEADER_OK) + throw std::runtime_error("JPEG Reader: failed to read header"); + + jpeg_start_decompress(&cinfo); + + if (cinfo.out_color_space == JCS_UNKNOWN) + throw std::runtime_error("JPEG Reader: failed to read unknown color space"); + + if (cinfo.output_width == 0 || cinfo.output_height == 0) + throw std::runtime_error("JPEG Reader: failed to read image size"); + + size_t width = cinfo.output_width; + size_t height = cinfo.output_height; + size_t components = cinfo.output_components; + size_t rowStride = components * width; + + PremultipliedImage image({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); + uint8_t* dst = image.data.get(); + + JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, rowStride, 1); + + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, buffer, 1); + + for (size_t i = 0; i < width; ++i) { + dst[0] = buffer[0][components * i]; + dst[3] = 0xFF; + + if (components > 2) { + dst[1] = buffer[0][components * i + 1]; + dst[2] = buffer[0][components * i + 2]; + } else { + dst[1] = dst[0]; + dst[2] = dst[0]; + } + + dst += 4; + } + } + + jpeg_finish_decompress(&cinfo); + + return image; +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/logging_stderr.cpp b/platform/default/src/mbgl/util/logging_stderr.cpp new file mode 100644 index 0000000000..41585fb7bb --- /dev/null +++ b/platform/default/src/mbgl/util/logging_stderr.cpp @@ -0,0 +1,12 @@ +#include <mbgl/util/logging.hpp> +#include <mbgl/util/enum.hpp> + +#include <iostream> + +namespace mbgl { + +void Log::platformRecord(EventSeverity severity, const std::string &msg) { + std::cerr << "[" << Enum<EventSeverity>::toString(severity) << "] " << msg << std::endl; +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/png_reader.cpp b/platform/default/src/mbgl/util/png_reader.cpp new file mode 100644 index 0000000000..4d4ee29d1f --- /dev/null +++ b/platform/default/src/mbgl/util/png_reader.cpp @@ -0,0 +1,142 @@ +#include <mbgl/util/image.hpp> +#include <mbgl/util/premultiply.hpp> +#include <mbgl/util/char_array_buffer.hpp> +#include <mbgl/util/logging.hpp> + +#include <istream> +#include <sstream> + +extern "C" +{ +#include <png.h> +} + +template<size_t max, typename... Args> +static std::string sprintf(const char *msg, Args... args) { + char res[max]; + int len = snprintf(res, sizeof(res), msg, args...); + return std::string(res, len); +} + +const static bool png_version_check __attribute__((unused)) = []() { + const png_uint_32 version = png_access_version_number(); + if (version != PNG_LIBPNG_VER) { + throw std::runtime_error(sprintf<96>( + "libpng version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d", + PNG_LIBPNG_VER / 10000, (PNG_LIBPNG_VER / 100) % 100, PNG_LIBPNG_VER % 100, + version / 10000, (version / 100) % 100, version % 100)); + } + return true; +}(); + +namespace mbgl { + +static void user_error_fn(png_structp, png_const_charp error_msg) { + throw std::runtime_error(std::string("failed to read invalid png: '") + error_msg + "'"); +} + +static void user_warning_fn(png_structp, png_const_charp warning_msg) { + Log::Warning(Event::Image, "ImageReader (PNG): %s", warning_msg); +} + +static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { + auto* fin = reinterpret_cast<std::istream*>(png_get_io_ptr(png_ptr)); + fin->read(reinterpret_cast<char*>(data), length); + std::streamsize read_count = fin->gcount(); + if (read_count < 0 || static_cast<png_size_t>(read_count) != length) + { + png_error(png_ptr, "Read Error"); + } +} + +struct png_struct_guard { + png_struct_guard(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) + : p_(png_ptr_ptr), + i_(info_ptr_ptr) {} + + ~png_struct_guard() { + png_destroy_read_struct(p_,i_,nullptr); + } + + png_structpp p_; + png_infopp i_; +}; + +PremultipliedImage decodePNG(const uint8_t* data, size_t size) { + util::CharArrayBuffer dataBuffer { reinterpret_cast<const char*>(data), size }; + std::istream stream(&dataBuffer); + + png_byte header[8] = { 0 }; + stream.read(reinterpret_cast<char*>(header), 8); + if (stream.gcount() != 8) + throw std::runtime_error("PNG reader: Could not read image"); + + int is_png = !png_sig_cmp(header, 0, 8); + if (!is_png) + throw std::runtime_error("File or stream is not a png"); + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_ptr) + throw std::runtime_error("failed to allocate png_ptr"); + + // catch errors in a custom way to avoid the need for setjmp + png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn); + + png_infop info_ptr; + png_struct_guard sguard(&png_ptr, &info_ptr); + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + throw std::runtime_error("failed to create info_ptr"); + + png_set_read_fn(png_ptr, &stream, png_read_data); + png_set_sig_bytes(png_ptr, 8); + png_read_info(png_ptr, info_ptr); + + png_uint_32 width = 0; + png_uint_32 height = 0; + int bit_depth = 0; + int color_type = 0; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr); + + UnassociatedImage image({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand(png_ptr); + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); + + if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) { + png_set_interlace_handling(png_ptr); // FIXME: libpng bug? + // according to docs png_read_image + // "..automatically handles interlacing, + // so you don't need to call png_set_interlace_handling()" + } + + png_read_update_info(png_ptr, info_ptr); + + // we can read whole image at once + // alloc row pointers + const std::unique_ptr<png_bytep[]> rows(new png_bytep[height]); + for (unsigned row = 0; row < height; ++row) + rows[row] = image.data.get() + row * width * 4; + png_read_image(png_ptr, rows.get()); + + png_read_end(png_ptr, nullptr); + + return util::premultiply(std::move(image)); +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/png_writer.cpp b/platform/default/src/mbgl/util/png_writer.cpp new file mode 100644 index 0000000000..b89e253f85 --- /dev/null +++ b/platform/default/src/mbgl/util/png_writer.cpp @@ -0,0 +1,77 @@ +#include <mbgl/util/compression.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/premultiply.hpp> + +#include <boost/crc.hpp> + +#include <cassert> +#include <cstring> + +#define NETWORK_BYTE_UINT32(value) \ + char(value >> 24), char(value >> 16), char(value >> 8), char(value >> 0) + +namespace { + +void addChunk(std::string& png, const char* type, const char* data = "", const uint32_t size = 0) { + assert(strlen(type) == 4); + + // Checksum encompasses type + data + boost::crc_32_type checksum; + checksum.process_bytes(type, 4); + checksum.process_bytes(data, size); + + const char length[4] = { NETWORK_BYTE_UINT32(size) }; + const char crc[4] = { NETWORK_BYTE_UINT32(checksum.checksum()) }; + + png.reserve(png.size() + 4 /* length */ + 4 /* type */ + size + 4 /* CRC */); + png.append(length, 4); + png.append(type, 4); + png.append(data, size); + png.append(crc, 4); +} + +} // namespace + +namespace mbgl { + +// Encode PNGs without libpng. +std::string encodePNG(const PremultipliedImage& pre) { + // Make copy of the image so that we can unpremultiply it. + const auto src = util::unpremultiply(pre.clone()); + + // PNG magic bytes + const char preamble[8] = { char(0x89), 'P', 'N', 'G', '\r', '\n', 0x1a, '\n' }; + + // IHDR chunk for our RGBA image. + const char ihdr[13] = { + NETWORK_BYTE_UINT32(src.size.width), // width + NETWORK_BYTE_UINT32(src.size.height), // height + 8, // bit depth == 8 bits + 6, // color type == RGBA + 0, // compression method == deflate + 0, // filter method == default + 0, // interlace method == none + }; + + // Prepare the (compressed) data chunk. + const auto stride = src.stride(); + std::string idat; + for (uint32_t y = 0; y < src.size.height; y++) { + // Every scanline needs to be prefixed with one byte that indicates the filter type. + idat.append(1, 0); // filter type 0 + idat.append((const char*)(src.data.get() + y * stride), stride); + } + idat = util::compress(idat); + + // Assemble the PNG. + std::string png; + png.reserve((8 /* preamble */) + (12 + 13 /* IHDR */) + + (12 + idat.size() /* IDAT */) + (12 /* IEND */)); + png.append(preamble, 8); + addChunk(png, "IHDR", ihdr, 13); + addChunk(png, "IDAT", idat.data(), static_cast<uint32_t>(idat.size())); + addChunk(png, "IEND"); + return png; +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/run_loop.cpp b/platform/default/src/mbgl/util/run_loop.cpp new file mode 100644 index 0000000000..868ee72114 --- /dev/null +++ b/platform/default/src/mbgl/util/run_loop.cpp @@ -0,0 +1,219 @@ +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/async_task.hpp> +#include <mbgl/util/thread_local.hpp> +#include <mbgl/actor/scheduler.hpp> + +#include <uv.h> + +#include <cassert> +#include <functional> +#include <unordered_map> + +namespace { + +void dummyCallback(uv_async_t*) {} + +} // namespace + +namespace mbgl { +namespace util { + +struct Watch { + static void onEvent(uv_poll_t* poll, int, int event) { + auto watch = reinterpret_cast<Watch*>(poll->data); + + RunLoop::Event watchEvent = RunLoop::Event::None; + switch (event) { + case UV_READABLE: + watchEvent = RunLoop::Event::Read; + break; + case UV_WRITABLE: + watchEvent = RunLoop::Event::Write; + break; + case UV_READABLE | UV_WRITABLE: + watchEvent = RunLoop::Event::ReadWrite; + break; + } + + watch->eventCallback(watch->fd, watchEvent); + }; + + static void onClose(uv_handle_t *poll) { + auto watch = reinterpret_cast<Watch*>(poll->data); + watch->closeCallback(); + }; + + uv_poll_t poll; + int fd; + + std::function<void(int, RunLoop::Event)> eventCallback; + std::function<void()> closeCallback; +}; + +RunLoop* RunLoop::Get() { + assert(static_cast<RunLoop*>(Scheduler::GetCurrent())); + return static_cast<RunLoop*>(Scheduler::GetCurrent()); +} + +class RunLoop::Impl { +public: + void closeHolder() { + uv_close(holderHandle(), [](uv_handle_t* h) { + delete reinterpret_cast<uv_async_t*>(h); + }); + } + + uv_handle_t* holderHandle() { + return reinterpret_cast<uv_handle_t*>(holder); + } + + uv_loop_t *loop = nullptr; + uv_async_t* holder = new uv_async_t; + + RunLoop::Type type; + std::unique_ptr<AsyncTask> async; + + std::unordered_map<int, std::unique_ptr<Watch>> watchPoll; +}; + +RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) { + switch (type) { + case Type::New: + impl->loop = new uv_loop_t; + if (uv_loop_init(impl->loop) != 0) { + throw std::runtime_error("Failed to initialize loop."); + } + break; + case Type::Default: + impl->loop = uv_default_loop(); + break; + } + + // Just for holding a ref to the main loop and keep + // it alive as required by libuv. + if (uv_async_init(impl->loop, impl->holder, dummyCallback) != 0) { + throw std::runtime_error("Failed to initialize async."); + } + + impl->type = type; + + Scheduler::SetCurrent(this); + impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this)); +} + +RunLoop::~RunLoop() { + Scheduler::SetCurrent(nullptr); + + // Close the dummy handle that we have + // just to keep the main loop alive. + impl->closeHolder(); + + if (impl->type == Type::Default) { + return; + } + + // Run the loop again to ensure that async + // close callbacks have been called. Not needed + // for the default main loop because it is only + // closed when the application exits. + impl->async.reset(); + runOnce(); + + if (uv_loop_close(impl->loop) == UV_EBUSY) { + assert(false && "Failed to close loop."); + } + delete impl->loop; +} + +LOOP_HANDLE RunLoop::getLoopHandle() { + return Get()->impl->loop; +} + +void RunLoop::wake() { + impl->async->send(); +} + +void RunLoop::run() { + MBGL_VERIFY_THREAD(tid); + + uv_ref(impl->holderHandle()); + uv_run(impl->loop, UV_RUN_DEFAULT); +} + +void RunLoop::runOnce() { + MBGL_VERIFY_THREAD(tid); + + uv_run(impl->loop, UV_RUN_NOWAIT); +} + +void RunLoop::stop() { + invoke([&] { uv_unref(impl->holderHandle()); }); +} + +void RunLoop::addWatch(int fd, Event event, std::function<void(int, Event)>&& callback) { + MBGL_VERIFY_THREAD(tid); + + Watch *watch = nullptr; + auto watchPollIter = impl->watchPoll.find(fd); + + if (watchPollIter == impl->watchPoll.end()) { + std::unique_ptr<Watch> watchPtr = std::make_unique<Watch>(); + + watch = watchPtr.get(); + impl->watchPoll[fd] = std::move(watchPtr); + + if (uv_poll_init(impl->loop, &watch->poll, fd)) { + throw std::runtime_error("Failed to init poll on file descriptor."); + } + } else { + watch = watchPollIter->second.get(); + } + + watch->poll.data = watch; + watch->fd = fd; + watch->eventCallback = std::move(callback); + + int pollEvent = 0; + switch (event) { + case Event::Read: + pollEvent = UV_READABLE; + break; + case Event::Write: + pollEvent = UV_WRITABLE; + break; + case Event::ReadWrite: + pollEvent = UV_READABLE | UV_WRITABLE; + break; + default: + throw std::runtime_error("Unhandled event."); + } + + if (uv_poll_start(&watch->poll, pollEvent, &Watch::onEvent)) { + throw std::runtime_error("Failed to start poll on file descriptor."); + } +} + +void RunLoop::removeWatch(int fd) { + MBGL_VERIFY_THREAD(tid); + + auto watchPollIter = impl->watchPoll.find(fd); + if (watchPollIter == impl->watchPoll.end()) { + return; + } + + Watch* watch = watchPollIter->second.release(); + impl->watchPoll.erase(watchPollIter); + + watch->closeCallback = [watch] { + delete watch; + }; + + if (uv_poll_stop(&watch->poll)) { + throw std::runtime_error("Failed to stop poll on file descriptor."); + } + + uv_close(reinterpret_cast<uv_handle_t*>(&watch->poll), &Watch::onClose); +} + +} // namespace util +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/shared_thread_pool.cpp b/platform/default/src/mbgl/util/shared_thread_pool.cpp new file mode 100644 index 0000000000..d7facbab94 --- /dev/null +++ b/platform/default/src/mbgl/util/shared_thread_pool.cpp @@ -0,0 +1,14 @@ +#include <mbgl/util/shared_thread_pool.hpp> + +namespace mbgl { + +std::shared_ptr<ThreadPool> sharedThreadPool() { + static std::weak_ptr<ThreadPool> weak; + auto pool = weak.lock(); + if (!pool) { + weak = pool = std::make_shared<ThreadPool>(4); + } + return pool; +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/string_stdlib.cpp b/platform/default/src/mbgl/util/string_stdlib.cpp new file mode 100644 index 0000000000..103444df1c --- /dev/null +++ b/platform/default/src/mbgl/util/string_stdlib.cpp @@ -0,0 +1,74 @@ +#include <mbgl/util/platform.hpp> +#include <libnu/casemap.h> +#include <cstring> +#include <sstream> + +namespace mbgl { namespace platform { + +std::string uppercase(const std::string& str) +{ + std::stringstream output; + char const *itr = str.c_str(), *nitr; + char const *end = itr + str.length(); + char lo[5] = { 0 }; + + for (; itr < end; itr = nitr) + { + uint32_t code_point = 0; + char const* buf = nullptr; + + nitr = _nu_toupper(itr, end, nu_utf8_read, &code_point, &buf, nullptr); + if (buf != nullptr) + { + do + { + buf = NU_CASEMAP_DECODING_FUNCTION(buf, &code_point); + if (code_point == 0) break; + output.write(lo, nu_utf8_write(code_point, lo) - lo); + } + while (code_point != 0); + } + else + { + output.write(itr, nitr - itr); + } + } + + return output.str(); + +} + +std::string lowercase(const std::string& str) +{ + std::stringstream output; + char const *itr = str.c_str(), *nitr; + char const *end = itr + str.length(); + char lo[5] = { 0 }; + + for (; itr < end; itr = nitr) + { + uint32_t code_point = 0; + char const* buf = nullptr; + + nitr = _nu_tolower(itr, end, nu_utf8_read, &code_point, &buf, nullptr); + if (buf != nullptr) + { + do + { + buf = NU_CASEMAP_DECODING_FUNCTION(buf, &code_point); + if (code_point == 0) break; + output.write(lo, nu_utf8_write(code_point, lo) - lo); + } + while (code_point != 0); + } + else + { + output.write(itr, nitr - itr); + } + } + + return output.str(); +} + +} // namespace platform +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/thread.cpp b/platform/default/src/mbgl/util/thread.cpp new file mode 100644 index 0000000000..c7c79b4fb0 --- /dev/null +++ b/platform/default/src/mbgl/util/thread.cpp @@ -0,0 +1,37 @@ +#include <mbgl/util/platform.hpp> +#include <mbgl/util/logging.hpp> + +#include <string> + +#include <pthread.h> +#include <sched.h> + +namespace mbgl { +namespace platform { + +std::string getCurrentThreadName() { + char name[32] = "unknown"; + pthread_getname_np(pthread_self(), name, sizeof(name)); + + return name; +} + +void setCurrentThreadName(const std::string& name) { + if (name.size() > 15) { // Linux hard limit (see manpages). + pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); + } else { + pthread_setname_np(pthread_self(), name.c_str()); + } +} + +void makeThreadLowPriority() { + struct sched_param param; + param.sched_priority = 0; + + if (sched_setscheduler(0, SCHED_IDLE, ¶m) != 0) { + Log::Warning(Event::General, "Couldn't set thread scheduling policy"); + } +} + +} // namespace platform +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/thread_local.cpp b/platform/default/src/mbgl/util/thread_local.cpp new file mode 100644 index 0000000000..db70773c12 --- /dev/null +++ b/platform/default/src/mbgl/util/thread_local.cpp @@ -0,0 +1,66 @@ +#include <mbgl/util/thread_local.hpp> + +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <stdexcept> +#include <cassert> + +#include <pthread.h> + +namespace mbgl { +namespace util { + +template <class T> +class ThreadLocal<T>::Impl { +public: + pthread_key_t key; +}; + +template <class T> +ThreadLocal<T>::ThreadLocal() : impl(std::make_unique<Impl>()) { + int ret = pthread_key_create(&impl->key, [](void *) {}); + + if (ret) { + throw std::runtime_error("Failed to init local storage key."); + } +} + +template <class T> +ThreadLocal<T>::~ThreadLocal() { + // ThreadLocal will not take ownership + // of the pointer it is managing. The pointer + // needs to be explicitly cleared before we + // destroy this object. + assert(!get()); + + if (pthread_key_delete(impl->key)) { + Log::Error(Event::General, "Failed to delete local storage key."); + assert(false); + } +} + +template <class T> +T* ThreadLocal<T>::get() { + auto* ret = reinterpret_cast<T*>(pthread_getspecific(impl->key)); + if (!ret) { + return nullptr; + } + + return ret; +} + +template <class T> +void ThreadLocal<T>::set(T* ptr) { + if (pthread_setspecific(impl->key, ptr)) { + throw std::runtime_error("Failed to set local storage."); + } +} + +template class ThreadLocal<BackendScope>; +template class ThreadLocal<Scheduler>; +template class ThreadLocal<int>; // For unit tests + +} // namespace util +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/timer.cpp b/platform/default/src/mbgl/util/timer.cpp new file mode 100644 index 0000000000..90a85bfc1f --- /dev/null +++ b/platform/default/src/mbgl/util/timer.cpp @@ -0,0 +1,73 @@ +#include <mbgl/util/timer.hpp> + +#include <mbgl/util/run_loop.hpp> + +#include <uv.h> + +namespace mbgl { +namespace util { + +class Timer::Impl { +public: + Impl() : timer(new uv_timer_t) { + auto* loop = reinterpret_cast<uv_loop_t*>(RunLoop::getLoopHandle()); + if (uv_timer_init(loop, timer) != 0) { + throw std::runtime_error("Failed to initialize timer."); + } + + handle()->data = this; + uv_unref(handle()); + } + + ~Impl() { + uv_close(handle(), [](uv_handle_t* h) { + delete reinterpret_cast<uv_timer_t*>(h); + }); + } + + void start(uint64_t timeout, uint64_t repeat, std::function<void ()>&& cb_) { + cb = std::move(cb_); + if (uv_timer_start(timer, timerCallback, timeout, repeat) != 0) { + throw std::runtime_error("Failed to start timer."); + } + } + + void stop() { + cb = nullptr; + if (uv_timer_stop(timer) != 0) { + throw std::runtime_error("Failed to stop timer."); + } + } + +private: + static void timerCallback(uv_timer_t* handle) { + reinterpret_cast<Impl*>(handle->data)->cb(); + } + + uv_handle_t* handle() { + return reinterpret_cast<uv_handle_t*>(timer); + } + + uv_timer_t* timer; + + std::function<void()> cb; +}; + +Timer::Timer() + : impl(std::make_unique<Impl>()) { +} + +Timer::~Timer() = default; + +void Timer::start(Duration timeout, Duration repeat, std::function<void()>&& cb) { + impl->start(std::chrono::duration_cast<Milliseconds>(timeout).count(), + std::chrono::duration_cast<Milliseconds>(repeat).count(), + std::move(cb)); +} + +void Timer::stop() { + impl->stop(); +} + +} // namespace util +} // namespace mbgl diff --git a/platform/default/src/mbgl/util/utf.cpp b/platform/default/src/mbgl/util/utf.cpp new file mode 100644 index 0000000000..f0f9d3e67a --- /dev/null +++ b/platform/default/src/mbgl/util/utf.cpp @@ -0,0 +1,17 @@ +#include <mbgl/util/utf.hpp> + +#include <boost/locale/encoding_utf.hpp> + +namespace mbgl { +namespace util { + +std::u16string convertUTF8ToUTF16(const std::string& str) { + return boost::locale::conv::utf_to_utf<char16_t>(str); +} + +std::string convertUTF16ToUTF8(const std::u16string& str) { + return boost::locale::conv::utf_to_utf<char>(str); +} + +} // namespace util +} // namespace mbgl |