[
  {
    "path": ".clang-format",
    "content": "BasedOnStyle: Google\nColumnLimit: 150\nStandard: Cpp11\n"
  },
  {
    "path": ".gitignore",
    "content": "/.idea/\n/BUILD/\n/examples/.env/\n*.so\nspdlog/\nCMakeCache.txt\nCMakeFiles/\nMakefile\ncmake_install.cmake\ntestclient\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.0)\n\nproject(librtcdcpp\n        VERSION 1.0.0\n        LANGUAGES CXX)\n\noption(DISABLE_SPDLOG \"Disable Spdlog\")\n\nset(CMAKE_CXX_STANDARD 14)\nset(CMAKE_MACOSX_RPATH 1)\n\n# Custom CMake modules\nset(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} \"${PROJECT_SOURCE_DIR}/cmake/Modules\")\n\n# Find packages\nset(THREADS_PREFER_PTHREAD_FLAG ON)\nfind_package(Threads REQUIRED)\nfind_package(OpenSSL REQUIRED)\nfind_package(LibNice REQUIRED)\nfind_package(UsrSCTP REQUIRED)\n\nset(LIB_HEADERS\n        include/rtcdcpp/Chunk.hpp\n        include/rtcdcpp/ChunkQueue.hpp\n        include/rtcdcpp/DataChannel.hpp\n        include/rtcdcpp/DTLSWrapper.hpp\n        include/rtcdcpp/Logging.hpp\n        include/rtcdcpp/NiceWrapper.hpp\n        include/rtcdcpp/PeerConnection.hpp\n        include/rtcdcpp/RTCCertificate.hpp\n        include/rtcdcpp/SCTPWrapper.hpp)\n\nset(LIB_SOURCES\n        src/DataChannel.cpp\n        src/DTLSWrapper.cpp\n        src/Logging.cpp\n        src/NiceWrapper.cpp\n        src/PeerConnection.cpp\n        src/RTCCertificate.cpp\n        src/SCTPWrapper.cpp)\n\nadd_library(rtcdcpp SHARED\n        ${LIB_HEADERS}\n        ${LIB_SOURCES})\n\nif (DISABLE_SPDLOG)\n    message(STATUS \"Spdlog is disabled. Use stubbed out logging\")\n    target_compile_definitions(rtcdcpp PUBLIC -DSPDLOG_DISABLED)\nelse ()\n    find_package(Spdlog REQUIRED)\n    target_link_libraries(rtcdcpp PUBLIC Gabime::Spdlog)\nendif ()\n\ntarget_include_directories(rtcdcpp\n        PUBLIC\n        ${PROJECT_SOURCE_DIR}/include)\n\ntarget_link_libraries(rtcdcpp\n        PUBLIC\n        LibNice::LibNice\n        SctpLab::UsrSCTP\n        OpenSSL::SSL\n        Threads::Threads)\n\n# Declare a namespaced alias for used in other projects\nadd_library(LibRtcdcpp::LibRtcdcpp ALIAS rtcdcpp)\n\n# Build examples\nadd_subdirectory(examples/websocket_client)\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n  * Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n  * Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in the\n    documentation and/or other materials provided with the distribution.\n  * Neither the name of the <organization> nor the\n    names of its contributors may be used to endorse or promote products\n    derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "librtcdcpp - A Simple WebRTC DataChannels Library\n=================================================\n\nlibrtcdcpp is a simple C++ implementation of the WebRTC DataChannels API.\n\nIt was originally written by [Andrew Gault](https://github.com/abgault) and [Nick Chadwick](https://github.com/chadnickbok), and was inspired in no small part by [librtcdc](https://github.com/xhs/librtcdc)\n\nIts goal is to be the easiest way to build native WebRTC DataChannels apps across PC/Mac/Linux/iOS/Android.\n\nWhy\n---\n\nBecause building the WebRTC libraries from Chromium can be a real PITA, and slimming it down to just DataChannels can be really tough.\n\n\nDependencies\n------------\n\n - libnice - https://github.com/libnice/libnice\n - usrsctp - https://github.com/sctplab/usrsctp\n - openssl - https://www.openssl.org/\n - spdlog  - https://github.com/gabime/spdlog. Header-only. Optional.\n\nBuilding\n--------\n\nOn Linux:\n\n**TODO**: deb and rpm packages\n\n  ./configure\n  make\n  sudo make install\n\nOn Mac:\n\n**TODO**: homebrew integration\n\n  brew install ...\n  ./configure\n  make\n  sudo make install\n\n\nOn Windows:\n\n**TODO**: Visual studio integration, or a script like that jsoncpp library does\n\n - We recommend you just copy-paste the cpp and hpp files into your own project and go from there\n\n\nLicensing\n---------\n\nBSD style - see the accompanying LICENSE file for more information\n"
  },
  {
    "path": "cmake/Modules/FindGLIB.cmake",
    "content": "# - Try to find Glib and its components (gio, gobject etc)\n# Once done, this will define\n#\n#  GLIB_FOUND - system has Glib\n#  GLIB_INCLUDE_DIRS - the Glib include directories\n#  GLIB_LIBRARIES - link these to use Glib\n#\n# Optionally, the COMPONENTS keyword can be passed to find_package()\n# and Glib components can be looked for.  Currently, the following\n# components can be used, and they define the following variables if\n# found:\n#\n#  gio:             GLIB_GIO_LIBRARIES\n#  gobject:         GLIB_GOBJECT_LIBRARIES\n#  gmodule:         GLIB_GMODULE_LIBRARIES\n#  gthread:         GLIB_GTHREAD_LIBRARIES\n#\n# Note that the respective _INCLUDE_DIR variables are not set, since\n# all headers are in the same directory as GLIB_INCLUDE_DIRS.\n#\n# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions\n# are met:\n# 1.  Redistributions of source code must retain the above copyright\n#     notice, this list of conditions and the following disclaimer.\n# 2.  Redistributions in binary form must reproduce the above copyright\n#     notice, this list of conditions and the following disclaimer in the\n#     documentation and/or other materials provided with the distribution.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS\n# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS\n# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfind_package(PkgConfig)\npkg_check_modules(PC_GLIB QUIET glib-2.0)\n\nfind_library(GLIB_LIBRARIES\n    NAMES glib-2.0\n    HINTS ${PC_GLIB_LIBDIR}\n          ${PC_GLIB_LIBRARY_DIRS}\n)\n\n# Files in glib's main include path may include glibconfig.h, which,\n# for some odd reason, is normally in $LIBDIR/glib-2.0/include.\nget_filename_component(_GLIB_LIBRARY_DIR ${GLIB_LIBRARIES} PATH)\nfind_path(GLIBCONFIG_INCLUDE_DIR\n    NAMES glibconfig.h\n    HINTS ${PC_LIBDIR} ${PC_LIBRARY_DIRS} ${_GLIB_LIBRARY_DIR}\n          ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS}\n    PATH_SUFFIXES glib-2.0/include\n)\n\nfind_path(GLIB_INCLUDE_DIR\n    NAMES glib.h\n    HINTS ${PC_GLIB_INCLUDEDIR}\n          ${PC_GLIB_INCLUDE_DIRS}\n    PATH_SUFFIXES glib-2.0\n)\n\nset(GLIB_INCLUDE_DIRS ${GLIB_INCLUDE_DIR} ${GLIBCONFIG_INCLUDE_DIR})\n\n# Version detection\nif (EXISTS \"${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h\")\n    file(READ \"${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h\" GLIBCONFIG_H_CONTENTS)\n    string(REGEX MATCH \"#define GLIB_MAJOR_VERSION ([0-9]+)\" _dummy \"${GLIBCONFIG_H_CONTENTS}\")\n    set(GLIB_VERSION_MAJOR \"${CMAKE_MATCH_1}\")\n    string(REGEX MATCH \"#define GLIB_MINOR_VERSION ([0-9]+)\" _dummy \"${GLIBCONFIG_H_CONTENTS}\")\n    set(GLIB_VERSION_MINOR \"${CMAKE_MATCH_1}\")\n    string(REGEX MATCH \"#define GLIB_MICRO_VERSION ([0-9]+)\" _dummy \"${GLIBCONFIG_H_CONTENTS}\")\n    set(GLIB_VERSION_MICRO \"${CMAKE_MATCH_1}\")\n    set(GLIB_VERSION \"${GLIB_VERSION_MAJOR}.${GLIB_VERSION_MINOR}.${GLIB_VERSION_MICRO}\")\nendif ()\n\n# Additional Glib components.  We only look for libraries, as not all of them\n# have corresponding headers and all headers are installed alongside the main\n# glib ones.\nforeach (_component ${GLIB_FIND_COMPONENTS})\n    if (${_component} STREQUAL \"gio\")\n        find_library(GLIB_GIO_LIBRARIES NAMES gio-2.0 HINTS ${_GLIB_LIBRARY_DIR})\n        set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GIO_LIBRARIES)\n    elseif (${_component} STREQUAL \"gobject\")\n        find_library(GLIB_GOBJECT_LIBRARIES NAMES gobject-2.0 HINTS ${_GLIB_LIBRARY_DIR})\n        set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GOBJECT_LIBRARIES)\n    elseif (${_component} STREQUAL \"gmodule\")\n        find_library(GLIB_GMODULE_LIBRARIES NAMES gmodule-2.0 HINTS ${_GLIB_LIBRARY_DIR})\n        set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GMODULE_LIBRARIES)\n    elseif (${_component} STREQUAL \"gthread\")\n        find_library(GLIB_GTHREAD_LIBRARIES NAMES gthread-2.0 HINTS ${_GLIB_LIBRARY_DIR})\n        set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GTHREAD_LIBRARIES)\n    elseif (${_component} STREQUAL \"gio-unix\")\n        # gio-unix is compiled as part of the gio library, but the include paths\n        # are separate from the shared glib ones. Since this is currently only used\n        # by WebKitGTK+ we don't go to extraordinary measures beyond pkg-config.\n        pkg_check_modules(GIO_UNIX QUIET gio-unix-2.0)\n    endif ()\nendforeach ()\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(GLIB REQUIRED_VARS GLIB_INCLUDE_DIRS GLIB_LIBRARIES ${ADDITIONAL_REQUIRED_VARS}\n                                       VERSION_VAR   GLIB_VERSION)\n\nmark_as_advanced(\n    GLIBCONFIG_INCLUDE_DIR\n    GLIB_GIO_LIBRARIES\n    GLIB_GIO_UNIX_LIBRARIES\n    GLIB_GMODULE_LIBRARIES\n    GLIB_GOBJECT_LIBRARIES\n    GLIB_GTHREAD_LIBRARIES\n    GLIB_INCLUDE_DIR\n    GLIB_INCLUDE_DIRS\n    GLIB_LIBRARIES\n)\n"
  },
  {
    "path": "cmake/Modules/FindLibNice.cmake",
    "content": "if (NOT TARGET LibNice::LibNice)\n    find_package(PkgConfig)\n    pkg_check_modules(PC_LIBNICE nice)\n    set(LIBNICE_DEFINITIONS ${PC_LIBNICE_CFLAGS_OTHER})\n\n    find_path(LIBNICE_INCLUDE_DIR nice/agent.h\n            HINTS ${PC_LIBNICE_INCLUDEDIR} ${PC_LIBNICE_INCLUDE_DIRS}\n            PATH_SUFFICES libnice)\n    find_library(LIBNICE_LIBRARY NAMES nice libnice\n            HINTS ${PC_LIBNICE_LIBDIR} ${PC_LIBNICE_LIBRARY_DIRS})\n\n    include(FindPackageHandleStandardArgs)\n    find_package_handle_standard_args(Libnice DEFAULT_MSG\n            LIBNICE_LIBRARY LIBNICE_INCLUDE_DIR)\n    mark_as_advanced(LIBNICE_INCLUDE_DIR LIBNICE_LIBRARY)\n\n    set(LIBNICE_LIBRARIES ${LIBNICE_LIBRARY})\n    set(LIBNICE_INCLUDE_DIRS ${LIBNICE_INCLUDE_DIR})\n\n    find_package(GLIB REQUIRED COMPONENTS gio gobject gmodule gthread)\n\n    list(APPEND LIBNICE_INCLUDE_DIRS ${GLIB_INCLUDE_DIRS})\n    list(APPEND LIBNICE_LIBRARIES ${GLIB_GOBJECT_LIBRARIES} ${GLIB_LIBRARIES})\n\n    if (LIBNICE_FOUND)\n        add_library(LibNice::LibNice UNKNOWN IMPORTED)\n        set_target_properties(LibNice::LibNice PROPERTIES\n                IMPORTED_LOCATION \"${LIBNICE_LIBRARY}\"\n                INTERFACE_COMPILE_DEFINITIONS \"_REENTRANT\"\n                INTERFACE_INCLUDE_DIRECTORIES \"${LIBNICE_INCLUDE_DIRS}\"\n                INTERFACE_LINK_LIBRARIES \"${LIBNICE_LIBRARIES}\"\n                IMPORTED_LINK_INTERFACE_LANGUAGES \"C\")\n    endif ()\nendif ()\n"
  },
  {
    "path": "cmake/Modules/FindSpdlog.cmake",
    "content": "if (NOT TARGET Gabime::Spdlog)\n    include(FindPackageHandleStandardArgs)\n    find_path(SPDLOG_INCLUDE_DIR NAMES spdlog/spdlog.h)\n    find_package_handle_standard_args(Spdlog DEFAULT_MSG SPDLOG_INCLUDE_DIR)\n    add_library(spdlog INTERFACE)\n    target_include_directories(spdlog INTERFACE ${SPDLOG_INCLUDE_DIR})\n    add_library(Gabime::Spdlog ALIAS spdlog)\nendif ()\n"
  },
  {
    "path": "cmake/Modules/FindUsrSCTP.cmake",
    "content": "# Simple libnice cmake find\n\nif (NOT TARGET SctpLab::UsrSCTP)\n    set(USRSCTP_DEFINITIONS INET INET6)\n    find_path(USRSCTP_INCLUDE_DIR usrsctp.h PATH_SUFFICES usrsctp)\n    find_library(USRSCTP_LIBRARY NAMES usrsctp libusrsctp)\n\n    include(FindPackageHandleStandardArgs)\n    find_package_handle_standard_args(Usrsctp DEFAULT_MSG USRSCTP_LIBRARY USRSCTP_INCLUDE_DIR)\n\n    mark_as_advanced(USRSCTP_INCLUDE_DIR USRSCTP_LIBRARY)\n\n    set(USRSCTP_LIBRARIES ${USRSCTP_LIBRARY})\n    set(USRSCTP_INCLUDE_DIRS ${USRSCTP_INCLUDE_DIR})\n\n    if (USRSCTP_FOUND)\n        add_library(SctpLab::UsrSCTP UNKNOWN IMPORTED)\n        set_target_properties(SctpLab::UsrSCTP PROPERTIES\n                IMPORTED_LOCATION \"${USRSCTP_LIBRARY}\"\n                INTERFACE_COMPILE_DEFINITIONS \"${USRSCTP_DEFINITIONS}\"\n                INTERFACE_INCLUDE_DIRECTORIES \"${USRSCTP_INCLUDE_DIRS}\"\n                IMPORTED_LINK_INTERFACE_LANGUAGES \"C\")\n    endif ()\nendif ()\n"
  },
  {
    "path": "examples/README.md",
    "content": "\n\nRunning the Demo\n----------------\n\nTo run the demo, first run:\n\n\tcd examples\n\tpython site-api.py\n\nThis should start a web server on localhost:5000\n\n\tOpen http://localhost:5000\n\tEnter channel name \"test\"\n\tClick \"connect\" - This should show up in the python console\n\nThen, run \n\n\tbuild/examples/websocket_client/testclient - its important that this be started after the web browser has connected to the test channel.\n\nYou should then see a whole heap of ICE messages, followed by a \"Hello from native code\"\n"
  },
  {
    "path": "examples/site-api.py",
    "content": "#!/usr/bin/env python\n# Sets up a basic site that can allow two browsers to connect to each\n# other via WebRTC DataChannels, sending connection events via WebSockets.\n\n\nfrom flask import Flask, send_from_directory\nfrom flask_sockets import Sockets\nimport json\n\napp = Flask(__name__)\nsockets = Sockets(app)\n\nchannels = {}\n\n@sockets.route('/channel/<name>')\ndef channel_socket(ws, name):\n    if name in channels:\n        channels[name].append(ws)\n    else:\n        channels[name] = [ws]\n\n    print(\"Got new websocket on channel\", name)\n\n    ws.send(json.dumps({\"type\": \"hello\", \"msg\": \"From the server\"}))\n\n    while not ws.closed:\n        message = ws.receive()\n        print(\"Got msg:\", message)\n\n        if message is None:\n            continue\n\n        for other_ws in channels[name]:\n            if ws is not other_ws:\n                other_ws.send(message)\n\n    channels[name].remove(ws)\n    for other_ws in channels[name]:\n        other_ws.send(json.dumps({\"type\": \"client_disconnected\", \"msg\": {}}))\n\n\n@app.route('/static/<path:path>')\ndef send_static(path):\n    return app.send_from_directory('static', path)\n\n\n@app.route('/')\ndef serve_site():\n    return app.send_static_file(\"index.html\")\n\n\nif __name__ == \"__main__\":\n    from gevent import pywsgi\n    from geventwebsocket.handler import WebSocketHandler\n    server = pywsgi.WSGIServer(('', 5000), app, handler_class=WebSocketHandler)\n    server.serve_forever()\n"
  },
  {
    "path": "examples/static/adapter.js",
    "content": "(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.adapter = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){\n /* eslint-env node */\n'use strict';\n\n// SDP helpers.\nvar SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n  return Math.random().toString(36).substr(2, 10);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n  return blob.trim().split('\\n').map(function(line) {\n    return line.trim();\n  });\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n  var parts = blob.split('\\nm=');\n  return parts.map(function(part, index) {\n    return (index > 0 ? 'm=' + part : part).trim() + '\\r\\n';\n  });\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n  return SDPUtils.splitLines(blob).filter(function(line) {\n    return line.indexOf(prefix) === 0;\n  });\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\nSDPUtils.parseCandidate = function(line) {\n  var parts;\n  // Parse both variants.\n  if (line.indexOf('a=candidate:') === 0) {\n    parts = line.substring(12).split(' ');\n  } else {\n    parts = line.substring(10).split(' ');\n  }\n\n  var candidate = {\n    foundation: parts[0],\n    component: parts[1],\n    protocol: parts[2].toLowerCase(),\n    priority: parseInt(parts[3], 10),\n    ip: parts[4],\n    port: parseInt(parts[5], 10),\n    // skip parts[6] == 'typ'\n    type: parts[7]\n  };\n\n  for (var i = 8; i < parts.length; i += 2) {\n    switch (parts[i]) {\n      case 'raddr':\n        candidate.relatedAddress = parts[i + 1];\n        break;\n      case 'rport':\n        candidate.relatedPort = parseInt(parts[i + 1], 10);\n        break;\n      case 'tcptype':\n        candidate.tcpType = parts[i + 1];\n        break;\n      default: // Unknown extensions are silently ignored.\n        break;\n    }\n  }\n  return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\nSDPUtils.writeCandidate = function(candidate) {\n  var sdp = [];\n  sdp.push(candidate.foundation);\n  sdp.push(candidate.component);\n  sdp.push(candidate.protocol.toUpperCase());\n  sdp.push(candidate.priority);\n  sdp.push(candidate.ip);\n  sdp.push(candidate.port);\n\n  var type = candidate.type;\n  sdp.push('typ');\n  sdp.push(type);\n  if (type !== 'host' && candidate.relatedAddress &&\n      candidate.relatedPort) {\n    sdp.push('raddr');\n    sdp.push(candidate.relatedAddress); // was: relAddr\n    sdp.push('rport');\n    sdp.push(candidate.relatedPort); // was: relPort\n  }\n  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n    sdp.push('tcptype');\n    sdp.push(candidate.tcpType);\n  }\n  return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n  var parts = line.substr(9).split(' ');\n  var parsed = {\n    payloadType: parseInt(parts.shift(), 10) // was: id\n  };\n\n  parts = parts[0].split('/');\n\n  parsed.name = parts[0];\n  parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n  // was: channels\n  parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n  return parsed;\n};\n\n// Generate an a=rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n      (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\\r\\n';\n};\n\n// Parses an a=extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n  var parts = line.substr(9).split(' ');\n  return {\n    id: parseInt(parts[0], 10),\n    uri: parts[1]\n  };\n};\n\n// Generates a=extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n       ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses an ftmp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n  var parsed = {};\n  var kv;\n  var parts = line.substr(line.indexOf(' ') + 1).split(';');\n  for (var j = 0; j < parts.length; j++) {\n    kv = parts[j].trim().split('=');\n    parsed[kv[0].trim()] = kv[1];\n  }\n  return parsed;\n};\n\n// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n  var line = '';\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  if (codec.parameters && Object.keys(codec.parameters).length) {\n    var params = [];\n    Object.keys(codec.parameters).forEach(function(param) {\n      params.push(param + '=' + codec.parameters[param]);\n    });\n    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n  }\n  return line;\n};\n\n// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n  var parts = line.substr(line.indexOf(' ') + 1).split(' ');\n  return {\n    type: parts.shift(),\n    parameter: parts.join(' ')\n  };\n};\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n  var lines = '';\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n    // FIXME: special handling for trr-int?\n    codec.rtcpFeedback.forEach(function(fb) {\n      lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + ' ' + fb.parameter +\n          '\\r\\n';\n    });\n  }\n  return lines;\n};\n\n// Parses an RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n  var sp = line.indexOf(' ');\n  var parts = {\n    ssrc: parseInt(line.substr(7, sp - 7), 10)\n  };\n  var colon = line.indexOf(':', sp);\n  if (colon > -1) {\n    parts.attribute = line.substr(sp + 1, colon - sp - 1);\n    parts.value = line.substr(colon + 1);\n  } else {\n    parts.attribute = line.substr(sp + 1);\n  }\n  return parts;\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n//   get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n  var lines = SDPUtils.splitLines(mediaSection);\n  // Search in session part, too.\n  lines = lines.concat(SDPUtils.splitLines(sessionpart));\n  var fpLine = lines.filter(function(line) {\n    return line.indexOf('a=fingerprint:') === 0;\n  })[0].substr(14);\n  // Note: a=setup line is ignored since we use the 'auto' role.\n  var dtlsParameters = {\n    role: 'auto',\n    fingerprints: [{\n      algorithm: fpLine.split(' ')[0],\n      value: fpLine.split(' ')[1]\n    }]\n  };\n  return dtlsParameters;\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n  var sdp = 'a=setup:' + setupType + '\\r\\n';\n  params.fingerprints.forEach(function(fp) {\n    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n  });\n  return sdp;\n};\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n//   get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n  var lines = SDPUtils.splitLines(mediaSection);\n  // Search in session part, too.\n  lines = lines.concat(SDPUtils.splitLines(sessionpart));\n  var iceParameters = {\n    usernameFragment: lines.filter(function(line) {\n      return line.indexOf('a=ice-ufrag:') === 0;\n    })[0].substr(12),\n    password: lines.filter(function(line) {\n      return line.indexOf('a=ice-pwd:') === 0;\n    })[0].substr(10)\n  };\n  return iceParameters;\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n  return 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n      'a=ice-pwd:' + params.password + '\\r\\n';\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n  var description = {\n    codecs: [],\n    headerExtensions: [],\n    fecMechanisms: [],\n    rtcp: []\n  };\n  var lines = SDPUtils.splitLines(mediaSection);\n  var mline = lines[0].split(' ');\n  for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n    var pt = mline[i];\n    var rtpmapline = SDPUtils.matchPrefix(\n        mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n    if (rtpmapline) {\n      var codec = SDPUtils.parseRtpMap(rtpmapline);\n      var fmtps = SDPUtils.matchPrefix(\n          mediaSection, 'a=fmtp:' + pt + ' ');\n      // Only the first a=fmtp:<pt> is considered.\n      codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n      codec.rtcpFeedback = SDPUtils.matchPrefix(\n          mediaSection, 'a=rtcp-fb:' + pt + ' ')\n        .map(SDPUtils.parseRtcpFb);\n      description.codecs.push(codec);\n      // parse FEC mechanisms from rtpmap lines.\n      switch (codec.name.toUpperCase()) {\n        case 'RED':\n        case 'ULPFEC':\n          description.fecMechanisms.push(codec.name.toUpperCase());\n          break;\n        default: // only RED and ULPFEC are recognized as FEC mechanisms.\n          break;\n      }\n    }\n  }\n  SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {\n    description.headerExtensions.push(SDPUtils.parseExtmap(line));\n  });\n  // FIXME: parse rtcp.\n  return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n  var sdp = '';\n\n  // Build the mline.\n  sdp += 'm=' + kind + ' ';\n  sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n  sdp += ' UDP/TLS/RTP/SAVPF ';\n  sdp += caps.codecs.map(function(codec) {\n    if (codec.preferredPayloadType !== undefined) {\n      return codec.preferredPayloadType;\n    }\n    return codec.payloadType;\n  }).join(' ') + '\\r\\n';\n\n  sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n  sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n  // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n  caps.codecs.forEach(function(codec) {\n    sdp += SDPUtils.writeRtpMap(codec);\n    sdp += SDPUtils.writeFmtp(codec);\n    sdp += SDPUtils.writeRtcpFb(codec);\n  });\n  // FIXME: add headerExtensions, fecMechanismş and rtcp.\n  sdp += 'a=rtcp-mux\\r\\n';\n  return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n  var encodingParameters = [];\n  var description = SDPUtils.parseRtpParameters(mediaSection);\n  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n  // filter a=ssrc:... cname:, ignore PlanB-msid\n  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n  .map(function(line) {\n    return SDPUtils.parseSsrcMedia(line);\n  })\n  .filter(function(parts) {\n    return parts.attribute === 'cname';\n  });\n  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n  var secondarySsrc;\n\n  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n  .map(function(line) {\n    var parts = line.split(' ');\n    parts.shift();\n    return parts.map(function(part) {\n      return parseInt(part, 10);\n    });\n  });\n  if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n    secondarySsrc = flows[0][1];\n  }\n\n  description.codecs.forEach(function(codec) {\n    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n      var encParam = {\n        ssrc: primarySsrc,\n        codecPayloadType: parseInt(codec.parameters.apt, 10),\n        rtx: {\n          payloadType: codec.payloadType,\n          ssrc: secondarySsrc\n        }\n      };\n      encodingParameters.push(encParam);\n      if (hasRed) {\n        encParam = JSON.parse(JSON.stringify(encParam));\n        encParam.fec = {\n          ssrc: secondarySsrc,\n          mechanism: hasUlpfec ? 'red+ulpfec' : 'red'\n        };\n        encodingParameters.push(encParam);\n      }\n    }\n  });\n  if (encodingParameters.length === 0 && primarySsrc) {\n    encodingParameters.push({\n      ssrc: primarySsrc\n    });\n  }\n\n  // we support both b=AS and b=TIAS but interpret AS as TIAS.\n  var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n  if (bandwidth.length) {\n    if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n      bandwidth = parseInt(bandwidth[0].substr(7), 10);\n    } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n      bandwidth = parseInt(bandwidth[0].substr(5), 10);\n    }\n    encodingParameters.forEach(function(params) {\n      params.maxBitrate = bandwidth;\n    });\n  }\n  return encodingParameters;\n};\n\nSDPUtils.writeSessionBoilerplate = function() {\n  // FIXME: sess-id should be an NTP timestamp.\n  return 'v=0\\r\\n' +\n      'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\\r\\n' +\n      's=-\\r\\n' +\n      't=0 0\\r\\n';\n};\n\nSDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {\n  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);\n\n  // Map ICE parameters (ufrag, pwd) to SDP.\n  sdp += SDPUtils.writeIceParameters(\n      transceiver.iceGatherer.getLocalParameters());\n\n  // Map DTLS parameters to SDP.\n  sdp += SDPUtils.writeDtlsParameters(\n      transceiver.dtlsTransport.getLocalParameters(),\n      type === 'offer' ? 'actpass' : 'active');\n\n  sdp += 'a=mid:' + transceiver.mid + '\\r\\n';\n\n  if (transceiver.rtpSender && transceiver.rtpReceiver) {\n    sdp += 'a=sendrecv\\r\\n';\n  } else if (transceiver.rtpSender) {\n    sdp += 'a=sendonly\\r\\n';\n  } else if (transceiver.rtpReceiver) {\n    sdp += 'a=recvonly\\r\\n';\n  } else {\n    sdp += 'a=inactive\\r\\n';\n  }\n\n  // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.\n  if (transceiver.rtpSender) {\n    var msid = 'msid:' + stream.id + ' ' +\n        transceiver.rtpSender.track.id + '\\r\\n';\n    sdp += 'a=' + msid;\n    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n        ' ' + msid;\n  }\n  // FIXME: this should be written by writeRtpDescription.\n  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n      ' cname:' + SDPUtils.localCName + '\\r\\n';\n  return sdp;\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n  // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n  var lines = SDPUtils.splitLines(mediaSection);\n  for (var i = 0; i < lines.length; i++) {\n    switch (lines[i]) {\n      case 'a=sendrecv':\n      case 'a=sendonly':\n      case 'a=recvonly':\n      case 'a=inactive':\n        return lines[i].substr(2);\n      default:\n        // FIXME: What should happen here?\n    }\n  }\n  if (sessionpart) {\n    return SDPUtils.getDirection(sessionpart);\n  }\n  return 'sendrecv';\n};\n\n// Expose public methods.\nmodule.exports = SDPUtils;\n\n},{}],2:[function(require,module,exports){\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n\n'use strict';\n\n// Shimming starts here.\n(function() {\n  // Utils.\n  var logging = require('./utils').log;\n  var browserDetails = require('./utils').browserDetails;\n  // Export to the adapter global object visible in the browser.\n  module.exports.browserDetails = browserDetails;\n  module.exports.extractVersion = require('./utils').extractVersion;\n  module.exports.disableLog = require('./utils').disableLog;\n\n  // Uncomment the line below if you want logging to occur, including logging\n  // for the switch statement below. Can also be turned on in the browser via\n  // adapter.disableLog(false), but then logging from the switch statement below\n  // will not appear.\n  // require('./utils').disableLog(false);\n\n  // Browser shims.\n  var chromeShim = require('./chrome/chrome_shim') || null;\n  var edgeShim = require('./edge/edge_shim') || null;\n  var firefoxShim = require('./firefox/firefox_shim') || null;\n  var safariShim = require('./safari/safari_shim') || null;\n\n  // Shim browser if found.\n  switch (browserDetails.browser) {\n    case 'opera': // fallthrough as it uses chrome shims\n    case 'chrome':\n      if (!chromeShim || !chromeShim.shimPeerConnection) {\n        logging('Chrome shim is not included in this adapter release.');\n        return;\n      }\n      logging('adapter.js shimming chrome.');\n      // Export to the adapter global object visible in the browser.\n      module.exports.browserShim = chromeShim;\n\n      chromeShim.shimGetUserMedia();\n      chromeShim.shimMediaStream();\n      chromeShim.shimSourceObject();\n      chromeShim.shimPeerConnection();\n      chromeShim.shimOnTrack();\n      break;\n    case 'firefox':\n      if (!firefoxShim || !firefoxShim.shimPeerConnection) {\n        logging('Firefox shim is not included in this adapter release.');\n        return;\n      }\n      logging('adapter.js shimming firefox.');\n      // Export to the adapter global object visible in the browser.\n      module.exports.browserShim = firefoxShim;\n\n      firefoxShim.shimGetUserMedia();\n      firefoxShim.shimSourceObject();\n      firefoxShim.shimPeerConnection();\n      firefoxShim.shimOnTrack();\n      break;\n    case 'edge':\n      if (!edgeShim || !edgeShim.shimPeerConnection) {\n        logging('MS edge shim is not included in this adapter release.');\n        return;\n      }\n      logging('adapter.js shimming edge.');\n      // Export to the adapter global object visible in the browser.\n      module.exports.browserShim = edgeShim;\n\n      edgeShim.shimGetUserMedia();\n      edgeShim.shimPeerConnection();\n      break;\n    case 'safari':\n      if (!safariShim) {\n        logging('Safari shim is not included in this adapter release.');\n        return;\n      }\n      logging('adapter.js shimming safari.');\n      // Export to the adapter global object visible in the browser.\n      module.exports.browserShim = safariShim;\n\n      safariShim.shimGetUserMedia();\n      break;\n    default:\n      logging('Unsupported browser!');\n  }\n})();\n\n},{\"./chrome/chrome_shim\":3,\"./edge/edge_shim\":5,\"./firefox/firefox_shim\":7,\"./safari/safari_shim\":9,\"./utils\":10}],3:[function(require,module,exports){\n\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\nvar logging = require('../utils.js').log;\nvar browserDetails = require('../utils.js').browserDetails;\n\nvar chromeShim = {\n  shimMediaStream: function() {\n    window.MediaStream = window.MediaStream || window.webkitMediaStream;\n  },\n\n  shimOnTrack: function() {\n    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in\n        window.RTCPeerConnection.prototype)) {\n      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {\n        get: function() {\n          return this._ontrack;\n        },\n        set: function(f) {\n          var self = this;\n          if (this._ontrack) {\n            this.removeEventListener('track', this._ontrack);\n            this.removeEventListener('addstream', this._ontrackpoly);\n          }\n          this.addEventListener('track', this._ontrack = f);\n          this.addEventListener('addstream', this._ontrackpoly = function(e) {\n            // onaddstream does not fire when a track is added to an existing\n            // stream. But stream.onaddtrack is implemented so we use that.\n            e.stream.addEventListener('addtrack', function(te) {\n              var event = new Event('track');\n              event.track = te.track;\n              event.receiver = {track: te.track};\n              event.streams = [e.stream];\n              self.dispatchEvent(event);\n            });\n            e.stream.getTracks().forEach(function(track) {\n              var event = new Event('track');\n              event.track = track;\n              event.receiver = {track: track};\n              event.streams = [e.stream];\n              this.dispatchEvent(event);\n            }.bind(this));\n          }.bind(this));\n        }\n      });\n    }\n  },\n\n  shimSourceObject: function() {\n    if (typeof window === 'object') {\n      if (window.HTMLMediaElement &&\n        !('srcObject' in window.HTMLMediaElement.prototype)) {\n        // Shim the srcObject property, once, when HTMLMediaElement is found.\n        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {\n          get: function() {\n            return this._srcObject;\n          },\n          set: function(stream) {\n            var self = this;\n            // Use _srcObject as a private property for this shim\n            this._srcObject = stream;\n            if (this.src) {\n              URL.revokeObjectURL(this.src);\n            }\n\n            if (!stream) {\n              this.src = '';\n              return;\n            }\n            this.src = URL.createObjectURL(stream);\n            // We need to recreate the blob url when a track is added or\n            // removed. Doing it manually since we want to avoid a recursion.\n            stream.addEventListener('addtrack', function() {\n              if (self.src) {\n                URL.revokeObjectURL(self.src);\n              }\n              self.src = URL.createObjectURL(stream);\n            });\n            stream.addEventListener('removetrack', function() {\n              if (self.src) {\n                URL.revokeObjectURL(self.src);\n              }\n              self.src = URL.createObjectURL(stream);\n            });\n          }\n        });\n      }\n    }\n  },\n\n  shimPeerConnection: function() {\n    // The RTCPeerConnection object.\n    window.RTCPeerConnection = function(pcConfig, pcConstraints) {\n      // Translate iceTransportPolicy to iceTransports,\n      // see https://code.google.com/p/webrtc/issues/detail?id=4869\n      logging('PeerConnection');\n      if (pcConfig && pcConfig.iceTransportPolicy) {\n        pcConfig.iceTransports = pcConfig.iceTransportPolicy;\n      }\n\n      var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);\n      var origGetStats = pc.getStats.bind(pc);\n      pc.getStats = function(selector, successCallback, errorCallback) {\n        var self = this;\n        var args = arguments;\n\n        // If selector is a function then we are in the old style stats so just\n        // pass back the original getStats format to avoid breaking old users.\n        if (arguments.length > 0 && typeof selector === 'function') {\n          return origGetStats(selector, successCallback);\n        }\n\n        var fixChromeStats_ = function(response) {\n          var standardReport = {};\n          var reports = response.result();\n          reports.forEach(function(report) {\n            var standardStats = {\n              id: report.id,\n              timestamp: report.timestamp,\n              type: report.type\n            };\n            report.names().forEach(function(name) {\n              standardStats[name] = report.stat(name);\n            });\n            standardReport[standardStats.id] = standardStats;\n          });\n\n          return standardReport;\n        };\n\n        // shim getStats with maplike support\n        var makeMapStats = function(stats, legacyStats) {\n          var map = new Map(Object.keys(stats).map(function(key) {\n            return[key, stats[key]];\n          }));\n          legacyStats = legacyStats || stats;\n          Object.keys(legacyStats).forEach(function(key) {\n            map[key] = legacyStats[key];\n          });\n          return map;\n        };\n\n        if (arguments.length >= 2) {\n          var successCallbackWrapper_ = function(response) {\n            args[1](makeMapStats(fixChromeStats_(response)));\n          };\n\n          return origGetStats.apply(this, [successCallbackWrapper_,\n              arguments[0]]);\n        }\n\n        // promise-support\n        return new Promise(function(resolve, reject) {\n          if (args.length === 1 && typeof selector === 'object') {\n            origGetStats.apply(self, [\n              function(response) {\n                resolve(makeMapStats(fixChromeStats_(response)));\n              }, reject]);\n          } else {\n            // Preserve legacy chrome stats only on legacy access of stats obj\n            origGetStats.apply(self, [\n              function(response) {\n                resolve(makeMapStats(fixChromeStats_(response),\n                    response.result()));\n              }, reject]);\n          }\n        }).then(successCallback, errorCallback);\n      };\n\n      return pc;\n    };\n    window.RTCPeerConnection.prototype = webkitRTCPeerConnection.prototype;\n\n    // wrap static methods. Currently just generateCertificate.\n    if (webkitRTCPeerConnection.generateCertificate) {\n      Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {\n        get: function() {\n          return webkitRTCPeerConnection.generateCertificate;\n        }\n      });\n    }\n\n    ['createOffer', 'createAnswer'].forEach(function(method) {\n      var nativeMethod = webkitRTCPeerConnection.prototype[method];\n      webkitRTCPeerConnection.prototype[method] = function() {\n        var self = this;\n        if (arguments.length < 1 || (arguments.length === 1 &&\n            typeof arguments[0] === 'object')) {\n          var opts = arguments.length === 1 ? arguments[0] : undefined;\n          return new Promise(function(resolve, reject) {\n            nativeMethod.apply(self, [resolve, reject, opts]);\n          });\n        }\n        return nativeMethod.apply(this, arguments);\n      };\n    });\n\n    // add promise support -- natively available in Chrome 51\n    if (browserDetails.version < 51) {\n      ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n          .forEach(function(method) {\n            var nativeMethod = webkitRTCPeerConnection.prototype[method];\n            webkitRTCPeerConnection.prototype[method] = function() {\n              var args = arguments;\n              var self = this;\n              var promise = new Promise(function(resolve, reject) {\n                nativeMethod.apply(self, [args[0], resolve, reject]);\n              });\n              if (args.length < 2) {\n                return promise;\n              }\n              return promise.then(function() {\n                args[1].apply(null, []);\n              },\n              function(err) {\n                if (args.length >= 3) {\n                  args[2].apply(null, [err]);\n                }\n              });\n            };\n          });\n    }\n\n    // support for addIceCandidate(null)\n    var nativeAddIceCandidate =\n        RTCPeerConnection.prototype.addIceCandidate;\n    RTCPeerConnection.prototype.addIceCandidate = function() {\n      return arguments[0] === null ? Promise.resolve()\n          : nativeAddIceCandidate.apply(this, arguments);\n    };\n\n    // shim implicit creation of RTCSessionDescription/RTCIceCandidate\n    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n        .forEach(function(method) {\n          var nativeMethod = webkitRTCPeerConnection.prototype[method];\n          webkitRTCPeerConnection.prototype[method] = function() {\n            arguments[0] = new ((method === 'addIceCandidate') ?\n                RTCIceCandidate : RTCSessionDescription)(arguments[0]);\n            return nativeMethod.apply(this, arguments);\n          };\n        });\n  },\n\n  // Attach a media stream to an element.\n  attachMediaStream: function(element, stream) {\n    logging('DEPRECATED, attachMediaStream will soon be removed.');\n    if (browserDetails.version >= 43) {\n      element.srcObject = stream;\n    } else if (typeof element.src !== 'undefined') {\n      element.src = URL.createObjectURL(stream);\n    } else {\n      logging('Error attaching stream to element.');\n    }\n  },\n\n  reattachMediaStream: function(to, from) {\n    logging('DEPRECATED, reattachMediaStream will soon be removed.');\n    if (browserDetails.version >= 43) {\n      to.srcObject = from.srcObject;\n    } else {\n      to.src = from.src;\n    }\n  }\n};\n\n\n// Expose public methods.\nmodule.exports = {\n  shimMediaStream: chromeShim.shimMediaStream,\n  shimOnTrack: chromeShim.shimOnTrack,\n  shimSourceObject: chromeShim.shimSourceObject,\n  shimPeerConnection: chromeShim.shimPeerConnection,\n  shimGetUserMedia: require('./getusermedia'),\n  attachMediaStream: chromeShim.attachMediaStream,\n  reattachMediaStream: chromeShim.reattachMediaStream\n};\n\n},{\"../utils.js\":10,\"./getusermedia\":4}],4:[function(require,module,exports){\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\nvar logging = require('../utils.js').log;\n\n// Expose public methods.\nmodule.exports = function() {\n  var constraintsToChrome_ = function(c) {\n    if (typeof c !== 'object' || c.mandatory || c.optional) {\n      return c;\n    }\n    var cc = {};\n    Object.keys(c).forEach(function(key) {\n      if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n        return;\n      }\n      var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};\n      if (r.exact !== undefined && typeof r.exact === 'number') {\n        r.min = r.max = r.exact;\n      }\n      var oldname_ = function(prefix, name) {\n        if (prefix) {\n          return prefix + name.charAt(0).toUpperCase() + name.slice(1);\n        }\n        return (name === 'deviceId') ? 'sourceId' : name;\n      };\n      if (r.ideal !== undefined) {\n        cc.optional = cc.optional || [];\n        var oc = {};\n        if (typeof r.ideal === 'number') {\n          oc[oldname_('min', key)] = r.ideal;\n          cc.optional.push(oc);\n          oc = {};\n          oc[oldname_('max', key)] = r.ideal;\n          cc.optional.push(oc);\n        } else {\n          oc[oldname_('', key)] = r.ideal;\n          cc.optional.push(oc);\n        }\n      }\n      if (r.exact !== undefined && typeof r.exact !== 'number') {\n        cc.mandatory = cc.mandatory || {};\n        cc.mandatory[oldname_('', key)] = r.exact;\n      } else {\n        ['min', 'max'].forEach(function(mix) {\n          if (r[mix] !== undefined) {\n            cc.mandatory = cc.mandatory || {};\n            cc.mandatory[oldname_(mix, key)] = r[mix];\n          }\n        });\n      }\n    });\n    if (c.advanced) {\n      cc.optional = (cc.optional || []).concat(c.advanced);\n    }\n    return cc;\n  };\n\n  var shimConstraints_ = function(constraints, func) {\n    constraints = JSON.parse(JSON.stringify(constraints));\n    if (constraints && constraints.audio) {\n      constraints.audio = constraintsToChrome_(constraints.audio);\n    }\n    if (constraints && typeof constraints.video === 'object') {\n      // Shim facingMode for mobile, where it defaults to \"user\".\n      var face = constraints.video.facingMode;\n      face = face && ((typeof face === 'object') ? face : {ideal: face});\n\n      if ((face && (face.exact === 'user' || face.exact === 'environment' ||\n                    face.ideal === 'user' || face.ideal === 'environment')) &&\n          !(navigator.mediaDevices.getSupportedConstraints &&\n            navigator.mediaDevices.getSupportedConstraints().facingMode)) {\n        delete constraints.video.facingMode;\n        if (face.exact === 'environment' || face.ideal === 'environment') {\n          // Look for \"back\" in label, or use last cam (typically back cam).\n          return navigator.mediaDevices.enumerateDevices()\n          .then(function(devices) {\n            devices = devices.filter(function(d) {\n              return d.kind === 'videoinput';\n            });\n            var back = devices.find(function(d) {\n              return d.label.toLowerCase().indexOf('back') !== -1;\n            }) || (devices.length && devices[devices.length - 1]);\n            if (back) {\n              constraints.video.deviceId = face.exact ? {exact: back.deviceId} :\n                                                        {ideal: back.deviceId};\n            }\n            constraints.video = constraintsToChrome_(constraints.video);\n            logging('chrome: ' + JSON.stringify(constraints));\n            return func(constraints);\n          });\n        }\n      }\n      constraints.video = constraintsToChrome_(constraints.video);\n    }\n    logging('chrome: ' + JSON.stringify(constraints));\n    return func(constraints);\n  };\n\n  var shimError_ = function(e) {\n    return {\n      name: {\n        PermissionDeniedError: 'NotAllowedError',\n        ConstraintNotSatisfiedError: 'OverconstrainedError'\n      }[e.name] || e.name,\n      message: e.message,\n      constraint: e.constraintName,\n      toString: function() {\n        return this.name + (this.message && ': ') + this.message;\n      }\n    };\n  };\n\n  var getUserMedia_ = function(constraints, onSuccess, onError) {\n    shimConstraints_(constraints, function(c) {\n      navigator.webkitGetUserMedia(c, onSuccess, function(e) {\n        onError(shimError_(e));\n      });\n    });\n  };\n\n  navigator.getUserMedia = getUserMedia_;\n\n  // Returns the result of getUserMedia as a Promise.\n  var getUserMediaPromise_ = function(constraints) {\n    return new Promise(function(resolve, reject) {\n      navigator.getUserMedia(constraints, resolve, reject);\n    });\n  };\n\n  if (!navigator.mediaDevices) {\n    navigator.mediaDevices = {\n      getUserMedia: getUserMediaPromise_,\n      enumerateDevices: function() {\n        return new Promise(function(resolve) {\n          var kinds = {audio: 'audioinput', video: 'videoinput'};\n          return MediaStreamTrack.getSources(function(devices) {\n            resolve(devices.map(function(device) {\n              return {label: device.label,\n                      kind: kinds[device.kind],\n                      deviceId: device.id,\n                      groupId: ''};\n            }));\n          });\n        });\n      }\n    };\n  }\n\n  // A shim for getUserMedia method on the mediaDevices object.\n  // TODO(KaptenJansson) remove once implemented in Chrome stable.\n  if (!navigator.mediaDevices.getUserMedia) {\n    navigator.mediaDevices.getUserMedia = function(constraints) {\n      return getUserMediaPromise_(constraints);\n    };\n  } else {\n    // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia\n    // function which returns a Promise, it does not accept spec-style\n    // constraints.\n    var origGetUserMedia = navigator.mediaDevices.getUserMedia.\n        bind(navigator.mediaDevices);\n    navigator.mediaDevices.getUserMedia = function(cs) {\n      return shimConstraints_(cs, function(c) {\n        return origGetUserMedia(c).catch(function(e) {\n          return Promise.reject(shimError_(e));\n        });\n      });\n    };\n  }\n\n  // Dummy devicechange event methods.\n  // TODO(KaptenJansson) remove once implemented in Chrome stable.\n  if (typeof navigator.mediaDevices.addEventListener === 'undefined') {\n    navigator.mediaDevices.addEventListener = function() {\n      logging('Dummy mediaDevices.addEventListener called.');\n    };\n  }\n  if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {\n    navigator.mediaDevices.removeEventListener = function() {\n      logging('Dummy mediaDevices.removeEventListener called.');\n    };\n  }\n};\n\n},{\"../utils.js\":10}],5:[function(require,module,exports){\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar SDPUtils = require('sdp');\nvar logging = require('../utils').log;\n\nvar edgeShim = {\n  shimPeerConnection: function() {\n    if (window.RTCIceGatherer) {\n      // ORTC defines an RTCIceCandidate object but no constructor.\n      // Not implemented in Edge.\n      if (!window.RTCIceCandidate) {\n        window.RTCIceCandidate = function(args) {\n          return args;\n        };\n      }\n      // ORTC does not have a session description object but\n      // other browsers (i.e. Chrome) that will support both PC and ORTC\n      // in the future might have this defined already.\n      if (!window.RTCSessionDescription) {\n        window.RTCSessionDescription = function(args) {\n          return args;\n        };\n      }\n    }\n\n    window.RTCPeerConnection = function(config) {\n      var self = this;\n\n      var _eventTarget = document.createDocumentFragment();\n      ['addEventListener', 'removeEventListener', 'dispatchEvent']\n          .forEach(function(method) {\n            self[method] = _eventTarget[method].bind(_eventTarget);\n          });\n\n      this.onicecandidate = null;\n      this.onaddstream = null;\n      this.ontrack = null;\n      this.onremovestream = null;\n      this.onsignalingstatechange = null;\n      this.oniceconnectionstatechange = null;\n      this.onnegotiationneeded = null;\n      this.ondatachannel = null;\n\n      this.localStreams = [];\n      this.remoteStreams = [];\n      this.getLocalStreams = function() {\n        return self.localStreams;\n      };\n      this.getRemoteStreams = function() {\n        return self.remoteStreams;\n      };\n\n      this.localDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.remoteDescription = new RTCSessionDescription({\n        type: '',\n        sdp: ''\n      });\n      this.signalingState = 'stable';\n      this.iceConnectionState = 'new';\n      this.iceGatheringState = 'new';\n\n      this.iceOptions = {\n        gatherPolicy: 'all',\n        iceServers: []\n      };\n      if (config && config.iceTransportPolicy) {\n        switch (config.iceTransportPolicy) {\n          case 'all':\n          case 'relay':\n            this.iceOptions.gatherPolicy = config.iceTransportPolicy;\n            break;\n          case 'none':\n            // FIXME: remove once implementation and spec have added this.\n            throw new TypeError('iceTransportPolicy \"none\" not supported');\n          default:\n            // don't set iceTransportPolicy.\n            break;\n        }\n      }\n      this.usingBundle = config && config.bundlePolicy === 'max-bundle';\n\n      if (config && config.iceServers) {\n        // Edge does not like\n        // 1) stun:\n        // 2) turn: that does not have all of turn:host:port?transport=udp\n        var iceServers = JSON.parse(JSON.stringify(config.iceServers));\n        this.iceOptions.iceServers = iceServers.filter(function(server) {\n          if (server && server.urls) {\n            var urls = server.urls;\n            if (typeof urls === 'string') {\n              urls = [urls];\n            }\n            urls = urls.filter(function(url) {\n              return url.indexOf('turn:') === 0 &&\n                  url.indexOf('transport=udp') !== -1;\n            })[0];\n            return !!urls;\n          }\n          return false;\n        });\n      }\n\n      // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n      // everything that is needed to describe a SDP m-line.\n      this.transceivers = [];\n\n      // since the iceGatherer is currently created in createOffer but we\n      // must not emit candidates until after setLocalDescription we buffer\n      // them in this array.\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {\n      var self = this;\n      var sections = SDPUtils.splitSections(self.localDescription.sdp);\n      // FIXME: need to apply ice candidates in a way which is async but\n      // in-order\n      this._localIceCandidatesBuffer.forEach(function(event) {\n        var end = !event.candidate || Object.keys(event.candidate).length === 0;\n        if (end) {\n          for (var j = 1; j < sections.length; j++) {\n            if (sections[j].indexOf('\\r\\na=end-of-candidates\\r\\n') === -1) {\n              sections[j] += 'a=end-of-candidates\\r\\n';\n            }\n          }\n        } else if (event.candidate.candidate.indexOf('typ endOfCandidates')\n            === -1) {\n          sections[event.candidate.sdpMLineIndex + 1] +=\n              'a=' + event.candidate.candidate + '\\r\\n';\n        }\n        self.localDescription.sdp = sections.join('');\n        self.dispatchEvent(event);\n        if (self.onicecandidate !== null) {\n          self.onicecandidate(event);\n        }\n        if (!event.candidate && self.iceGatheringState !== 'complete') {\n          var complete = self.transceivers.every(function(transceiver) {\n            return transceiver.iceGatherer &&\n                transceiver.iceGatherer.state === 'completed';\n          });\n          if (complete) {\n            self.iceGatheringState = 'complete';\n          }\n        }\n      });\n      this._localIceCandidatesBuffer = [];\n    };\n\n    window.RTCPeerConnection.prototype.addStream = function(stream) {\n      // Clone is necessary for local demos mostly, attaching directly\n      // to two different senders does not work (build 10547).\n      this.localStreams.push(stream.clone());\n      this._maybeFireNegotiationNeeded();\n    };\n\n    window.RTCPeerConnection.prototype.removeStream = function(stream) {\n      var idx = this.localStreams.indexOf(stream);\n      if (idx > -1) {\n        this.localStreams.splice(idx, 1);\n        this._maybeFireNegotiationNeeded();\n      }\n    };\n\n    window.RTCPeerConnection.prototype.getSenders = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpSender;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpSender;\n      });\n    };\n\n    window.RTCPeerConnection.prototype.getReceivers = function() {\n      return this.transceivers.filter(function(transceiver) {\n        return !!transceiver.rtpReceiver;\n      })\n      .map(function(transceiver) {\n        return transceiver.rtpReceiver;\n      });\n    };\n\n    // Determines the intersection of local and remote capabilities.\n    window.RTCPeerConnection.prototype._getCommonCapabilities =\n        function(localCapabilities, remoteCapabilities) {\n          var commonCapabilities = {\n            codecs: [],\n            headerExtensions: [],\n            fecMechanisms: []\n          };\n          localCapabilities.codecs.forEach(function(lCodec) {\n            for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n              var rCodec = remoteCapabilities.codecs[i];\n              if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n                  lCodec.clockRate === rCodec.clockRate &&\n                  lCodec.numChannels === rCodec.numChannels) {\n                // push rCodec so we reply with offerer payload type\n                commonCapabilities.codecs.push(rCodec);\n\n                // FIXME: also need to determine intersection between\n                // .rtcpFeedback and .parameters\n                break;\n              }\n            }\n          });\n\n          localCapabilities.headerExtensions\n              .forEach(function(lHeaderExtension) {\n                for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n                     i++) {\n                  var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n                  if (lHeaderExtension.uri === rHeaderExtension.uri) {\n                    commonCapabilities.headerExtensions.push(rHeaderExtension);\n                    break;\n                  }\n                }\n              });\n\n          // FIXME: fecMechanisms\n          return commonCapabilities;\n        };\n\n    // Create ICE gatherer, ICE transport and DTLS transport.\n    window.RTCPeerConnection.prototype._createIceAndDtlsTransports =\n        function(mid, sdpMLineIndex) {\n          var self = this;\n          var iceGatherer = new RTCIceGatherer(self.iceOptions);\n          var iceTransport = new RTCIceTransport(iceGatherer);\n          iceGatherer.onlocalcandidate = function(evt) {\n            var event = new Event('icecandidate');\n            event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n            var cand = evt.candidate;\n            var end = !cand || Object.keys(cand).length === 0;\n            // Edge emits an empty object for RTCIceCandidateComplete‥\n            if (end) {\n              // polyfill since RTCIceGatherer.state is not implemented in\n              // Edge 10547 yet.\n              if (iceGatherer.state === undefined) {\n                iceGatherer.state = 'completed';\n              }\n\n              // Emit a candidate with type endOfCandidates to make the samples\n              // work. Edge requires addIceCandidate with this empty candidate\n              // to start checking. The real solution is to signal\n              // end-of-candidates to the other side when getting the null\n              // candidate but some apps (like the samples) don't do that.\n              event.candidate.candidate =\n                  'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';\n            } else {\n              // RTCIceCandidate doesn't have a component, needs to be added\n              cand.component = iceTransport.component === 'RTCP' ? 2 : 1;\n              event.candidate.candidate = SDPUtils.writeCandidate(cand);\n            }\n\n            // update local description.\n            var sections = SDPUtils.splitSections(self.localDescription.sdp);\n            if (event.candidate.candidate.indexOf('typ endOfCandidates')\n                === -1) {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=' + event.candidate.candidate + '\\r\\n';\n            } else {\n              sections[event.candidate.sdpMLineIndex + 1] +=\n                  'a=end-of-candidates\\r\\n';\n            }\n            self.localDescription.sdp = sections.join('');\n\n            var complete = self.transceivers.every(function(transceiver) {\n              return transceiver.iceGatherer &&\n                  transceiver.iceGatherer.state === 'completed';\n            });\n\n            // Emit candidate if localDescription is set.\n            // Also emits null candidate when all gatherers are complete.\n            switch (self.iceGatheringState) {\n              case 'new':\n                self._localIceCandidatesBuffer.push(event);\n                if (end && complete) {\n                  self._localIceCandidatesBuffer.push(\n                      new Event('icecandidate'));\n                }\n                break;\n              case 'gathering':\n                self._emitBufferedCandidates();\n                self.dispatchEvent(event);\n                if (self.onicecandidate !== null) {\n                  self.onicecandidate(event);\n                }\n                if (complete) {\n                  self.dispatchEvent(new Event('icecandidate'));\n                  if (self.onicecandidate !== null) {\n                    self.onicecandidate(new Event('icecandidate'));\n                  }\n                  self.iceGatheringState = 'complete';\n                }\n                break;\n              case 'complete':\n                // should not happen... currently!\n                break;\n              default: // no-op.\n                break;\n            }\n          };\n          iceTransport.onicestatechange = function() {\n            self._updateConnectionState();\n          };\n\n          var dtlsTransport = new RTCDtlsTransport(iceTransport);\n          dtlsTransport.ondtlsstatechange = function() {\n            self._updateConnectionState();\n          };\n          dtlsTransport.onerror = function() {\n            // onerror does not set state to failed by itself.\n            dtlsTransport.state = 'failed';\n            self._updateConnectionState();\n          };\n\n          return {\n            iceGatherer: iceGatherer,\n            iceTransport: iceTransport,\n            dtlsTransport: dtlsTransport\n          };\n        };\n\n    // Start the RTP Sender and Receiver for a transceiver.\n    window.RTCPeerConnection.prototype._transceive = function(transceiver,\n        send, recv) {\n      var params = this._getCommonCapabilities(transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n      if (send && transceiver.rtpSender) {\n        params.encodings = transceiver.sendEncodingParameters;\n        params.rtcp = {\n          cname: SDPUtils.localCName\n        };\n        if (transceiver.recvEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpSender.send(params);\n      }\n      if (recv && transceiver.rtpReceiver) {\n        params.encodings = transceiver.recvEncodingParameters;\n        params.rtcp = {\n          cname: transceiver.cname\n        };\n        if (transceiver.sendEncodingParameters.length) {\n          params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n        }\n        transceiver.rtpReceiver.receive(params);\n      }\n    };\n\n    window.RTCPeerConnection.prototype.setLocalDescription =\n        function(description) {\n          var self = this;\n          var sections;\n          var sessionpart;\n          if (description.type === 'offer') {\n            // FIXME: What was the purpose of this empty if statement?\n            // if (!this._pendingOffer) {\n            // } else {\n            if (this._pendingOffer) {\n              // VERY limited support for SDP munging. Limited to:\n              // * changing the order of codecs\n              sections = SDPUtils.splitSections(description.sdp);\n              sessionpart = sections.shift();\n              sections.forEach(function(mediaSection, sdpMLineIndex) {\n                var caps = SDPUtils.parseRtpParameters(mediaSection);\n                self._pendingOffer[sdpMLineIndex].localCapabilities = caps;\n              });\n              this.transceivers = this._pendingOffer;\n              delete this._pendingOffer;\n            }\n          } else if (description.type === 'answer') {\n            sections = SDPUtils.splitSections(self.remoteDescription.sdp);\n            sessionpart = sections.shift();\n            var isIceLite = SDPUtils.matchPrefix(sessionpart,\n                'a=ice-lite').length > 0;\n            sections.forEach(function(mediaSection, sdpMLineIndex) {\n              var transceiver = self.transceivers[sdpMLineIndex];\n              var iceGatherer = transceiver.iceGatherer;\n              var iceTransport = transceiver.iceTransport;\n              var dtlsTransport = transceiver.dtlsTransport;\n              var localCapabilities = transceiver.localCapabilities;\n              var remoteCapabilities = transceiver.remoteCapabilities;\n              var rejected = mediaSection.split('\\n', 1)[0]\n                  .split(' ', 2)[1] === '0';\n\n              if (!rejected) {\n                var remoteIceParameters = SDPUtils.getIceParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                  .map(function(cand) {\n                    return SDPUtils.parseCandidate(cand);\n                  })\n                  .filter(function(cand) {\n                    return cand.component === '1';\n                  });\n                  // ice-lite only includes host candidates in the SDP so we can\n                  // use setRemoteCandidates (which implies an\n                  // RTCIceCandidateComplete)\n                  if (cands.length) {\n                    iceTransport.setRemoteCandidates(cands);\n                  }\n                }\n                var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n                    mediaSection, sessionpart);\n                if (isIceLite) {\n                  remoteDtlsParameters.role = 'server';\n                }\n\n                if (!self.usingBundle || sdpMLineIndex === 0) {\n                  iceTransport.start(iceGatherer, remoteIceParameters,\n                      isIceLite ? 'controlling' : 'controlled');\n                  dtlsTransport.start(remoteDtlsParameters);\n                }\n\n                // Calculate intersection of capabilities.\n                var params = self._getCommonCapabilities(localCapabilities,\n                    remoteCapabilities);\n\n                // Start the RTCRtpSender. The RTCRtpReceiver for this\n                // transceiver has already been started in setRemoteDescription.\n                self._transceive(transceiver,\n                    params.codecs.length > 0,\n                    false);\n              }\n            });\n          }\n\n          this.localDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-local-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n\n          // If a success callback was provided, emit ICE candidates after it\n          // has been executed. Otherwise, emit callback after the Promise is\n          // resolved.\n          var hasCallback = arguments.length > 1 &&\n            typeof arguments[1] === 'function';\n          if (hasCallback) {\n            var cb = arguments[1];\n            window.setTimeout(function() {\n              cb();\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              self._emitBufferedCandidates();\n            }, 0);\n          }\n          var p = Promise.resolve();\n          p.then(function() {\n            if (!hasCallback) {\n              if (self.iceGatheringState === 'new') {\n                self.iceGatheringState = 'gathering';\n              }\n              // Usually candidates will be emitted earlier.\n              window.setTimeout(self._emitBufferedCandidates.bind(self), 500);\n            }\n          });\n          return p;\n        };\n\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n        function(description) {\n          var self = this;\n          var stream = new MediaStream();\n          var receiverList = [];\n          var sections = SDPUtils.splitSections(description.sdp);\n          var sessionpart = sections.shift();\n          var isIceLite = SDPUtils.matchPrefix(sessionpart,\n              'a=ice-lite').length > 0;\n          this.usingBundle = SDPUtils.matchPrefix(sessionpart,\n              'a=group:BUNDLE ').length > 0;\n          sections.forEach(function(mediaSection, sdpMLineIndex) {\n            var lines = SDPUtils.splitLines(mediaSection);\n            var mline = lines[0].substr(2).split(' ');\n            var kind = mline[0];\n            var rejected = mline[1] === '0';\n            var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n\n            var transceiver;\n            var iceGatherer;\n            var iceTransport;\n            var dtlsTransport;\n            var rtpSender;\n            var rtpReceiver;\n            var sendEncodingParameters;\n            var recvEncodingParameters;\n            var localCapabilities;\n\n            var track;\n            // FIXME: ensure the mediaSection has rtcp-mux set.\n            var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n            var remoteIceParameters;\n            var remoteDtlsParameters;\n            if (!rejected) {\n              remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n                  sessionpart);\n              remoteDtlsParameters.role = 'client';\n            }\n            recvEncodingParameters =\n                SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n            var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');\n            if (mid.length) {\n              mid = mid[0].substr(6);\n            } else {\n              mid = SDPUtils.generateIdentifier();\n            }\n\n            var cname;\n            // Gets the first SSRC. Note that with RTX there might be multiple\n            // SSRCs.\n            var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n                .map(function(line) {\n                  return SDPUtils.parseSsrcMedia(line);\n                })\n                .filter(function(obj) {\n                  return obj.attribute === 'cname';\n                })[0];\n            if (remoteSsrc) {\n              cname = remoteSsrc.value;\n            }\n\n            var isComplete = SDPUtils.matchPrefix(mediaSection,\n                'a=end-of-candidates').length > 0;\n            var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n                .map(function(cand) {\n                  return SDPUtils.parseCandidate(cand);\n                })\n                .filter(function(cand) {\n                  return cand.component === '1';\n                });\n            if (description.type === 'offer' && !rejected) {\n              var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n                iceGatherer: self.transceivers[0].iceGatherer,\n                iceTransport: self.transceivers[0].iceTransport,\n                dtlsTransport: self.transceivers[0].dtlsTransport\n              } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n              if (isComplete) {\n                transports.iceTransport.setRemoteCandidates(cands);\n              }\n\n              localCapabilities = RTCRtpReceiver.getCapabilities(kind);\n              sendEncodingParameters = [{\n                ssrc: (2 * sdpMLineIndex + 2) * 1001\n              }];\n\n              rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n\n              track = rtpReceiver.track;\n              receiverList.push([track, rtpReceiver]);\n              // FIXME: not correct when there are multiple streams but that is\n              // not currently supported in this shim.\n              stream.addTrack(track);\n\n              // FIXME: look at direction.\n              if (self.localStreams.length > 0 &&\n                  self.localStreams[0].getTracks().length >= sdpMLineIndex) {\n                // FIXME: actually more complicated, needs to match types etc\n                var localtrack = self.localStreams[0]\n                    .getTracks()[sdpMLineIndex];\n                rtpSender = new RTCRtpSender(localtrack,\n                    transports.dtlsTransport);\n              }\n\n              self.transceivers[sdpMLineIndex] = {\n                iceGatherer: transports.iceGatherer,\n                iceTransport: transports.iceTransport,\n                dtlsTransport: transports.dtlsTransport,\n                localCapabilities: localCapabilities,\n                remoteCapabilities: remoteCapabilities,\n                rtpSender: rtpSender,\n                rtpReceiver: rtpReceiver,\n                kind: kind,\n                mid: mid,\n                cname: cname,\n                sendEncodingParameters: sendEncodingParameters,\n                recvEncodingParameters: recvEncodingParameters\n              };\n              // Start the RTCRtpReceiver now. The RTPSender is started in\n              // setLocalDescription.\n              self._transceive(self.transceivers[sdpMLineIndex],\n                  false,\n                  direction === 'sendrecv' || direction === 'sendonly');\n            } else if (description.type === 'answer' && !rejected) {\n              transceiver = self.transceivers[sdpMLineIndex];\n              iceGatherer = transceiver.iceGatherer;\n              iceTransport = transceiver.iceTransport;\n              dtlsTransport = transceiver.dtlsTransport;\n              rtpSender = transceiver.rtpSender;\n              rtpReceiver = transceiver.rtpReceiver;\n              sendEncodingParameters = transceiver.sendEncodingParameters;\n              localCapabilities = transceiver.localCapabilities;\n\n              self.transceivers[sdpMLineIndex].recvEncodingParameters =\n                  recvEncodingParameters;\n              self.transceivers[sdpMLineIndex].remoteCapabilities =\n                  remoteCapabilities;\n              self.transceivers[sdpMLineIndex].cname = cname;\n\n              if ((isIceLite || isComplete) && cands.length) {\n                iceTransport.setRemoteCandidates(cands);\n              }\n              if (!self.usingBundle || sdpMLineIndex === 0) {\n                iceTransport.start(iceGatherer, remoteIceParameters,\n                    'controlling');\n                dtlsTransport.start(remoteDtlsParameters);\n              }\n\n              self._transceive(transceiver,\n                  direction === 'sendrecv' || direction === 'recvonly',\n                  direction === 'sendrecv' || direction === 'sendonly');\n\n              if (rtpReceiver &&\n                  (direction === 'sendrecv' || direction === 'sendonly')) {\n                track = rtpReceiver.track;\n                receiverList.push([track, rtpReceiver]);\n                stream.addTrack(track);\n              } else {\n                // FIXME: actually the receiver should be created later.\n                delete transceiver.rtpReceiver;\n              }\n            }\n          });\n\n          this.remoteDescription = {\n            type: description.type,\n            sdp: description.sdp\n          };\n          switch (description.type) {\n            case 'offer':\n              this._updateSignalingState('have-remote-offer');\n              break;\n            case 'answer':\n              this._updateSignalingState('stable');\n              break;\n            default:\n              throw new TypeError('unsupported type \"' + description.type +\n                  '\"');\n          }\n          if (stream.getTracks().length) {\n            self.remoteStreams.push(stream);\n            window.setTimeout(function() {\n              var event = new Event('addstream');\n              event.stream = stream;\n              self.dispatchEvent(event);\n              if (self.onaddstream !== null) {\n                window.setTimeout(function() {\n                  self.onaddstream(event);\n                }, 0);\n              }\n\n              receiverList.forEach(function(item) {\n                var track = item[0];\n                var receiver = item[1];\n                var trackEvent = new Event('track');\n                trackEvent.track = track;\n                trackEvent.receiver = receiver;\n                trackEvent.streams = [stream];\n                self.dispatchEvent(event);\n                if (self.ontrack !== null) {\n                  window.setTimeout(function() {\n                    self.ontrack(trackEvent);\n                  }, 0);\n                }\n              });\n            }, 0);\n          }\n          if (arguments.length > 1 && typeof arguments[1] === 'function') {\n            window.setTimeout(arguments[1], 0);\n          }\n          return Promise.resolve();\n        };\n\n    window.RTCPeerConnection.prototype.close = function() {\n      this.transceivers.forEach(function(transceiver) {\n        /* not yet\n        if (transceiver.iceGatherer) {\n          transceiver.iceGatherer.close();\n        }\n        */\n        if (transceiver.iceTransport) {\n          transceiver.iceTransport.stop();\n        }\n        if (transceiver.dtlsTransport) {\n          transceiver.dtlsTransport.stop();\n        }\n        if (transceiver.rtpSender) {\n          transceiver.rtpSender.stop();\n        }\n        if (transceiver.rtpReceiver) {\n          transceiver.rtpReceiver.stop();\n        }\n      });\n      // FIXME: clean up tracks, local streams, remote streams, etc\n      this._updateSignalingState('closed');\n    };\n\n    // Update the signaling state.\n    window.RTCPeerConnection.prototype._updateSignalingState =\n        function(newState) {\n          this.signalingState = newState;\n          var event = new Event('signalingstatechange');\n          this.dispatchEvent(event);\n          if (this.onsignalingstatechange !== null) {\n            this.onsignalingstatechange(event);\n          }\n        };\n\n    // Determine whether to fire the negotiationneeded event.\n    window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =\n        function() {\n          // Fire away (for now).\n          var event = new Event('negotiationneeded');\n          this.dispatchEvent(event);\n          if (this.onnegotiationneeded !== null) {\n            this.onnegotiationneeded(event);\n          }\n        };\n\n    // Update the connection state.\n    window.RTCPeerConnection.prototype._updateConnectionState = function() {\n      var self = this;\n      var newState;\n      var states = {\n        'new': 0,\n        closed: 0,\n        connecting: 0,\n        checking: 0,\n        connected: 0,\n        completed: 0,\n        failed: 0\n      };\n      this.transceivers.forEach(function(transceiver) {\n        states[transceiver.iceTransport.state]++;\n        states[transceiver.dtlsTransport.state]++;\n      });\n      // ICETransport.completed and connected are the same for this purpose.\n      states.connected += states.completed;\n\n      newState = 'new';\n      if (states.failed > 0) {\n        newState = 'failed';\n      } else if (states.connecting > 0 || states.checking > 0) {\n        newState = 'connecting';\n      } else if (states.disconnected > 0) {\n        newState = 'disconnected';\n      } else if (states.new > 0) {\n        newState = 'new';\n      } else if (states.connected > 0 || states.completed > 0) {\n        newState = 'connected';\n      }\n\n      if (newState !== self.iceConnectionState) {\n        self.iceConnectionState = newState;\n        var event = new Event('iceconnectionstatechange');\n        this.dispatchEvent(event);\n        if (this.oniceconnectionstatechange !== null) {\n          this.oniceconnectionstatechange(event);\n        }\n      }\n    };\n\n    window.RTCPeerConnection.prototype.createOffer = function() {\n      var self = this;\n      if (this._pendingOffer) {\n        throw new Error('createOffer called while there is a pending offer.');\n      }\n      var offerOptions;\n      if (arguments.length === 1 && typeof arguments[0] !== 'function') {\n        offerOptions = arguments[0];\n      } else if (arguments.length === 3) {\n        offerOptions = arguments[2];\n      }\n\n      var tracks = [];\n      var numAudioTracks = 0;\n      var numVideoTracks = 0;\n      // Default to sendrecv.\n      if (this.localStreams.length) {\n        numAudioTracks = this.localStreams[0].getAudioTracks().length;\n        numVideoTracks = this.localStreams[0].getVideoTracks().length;\n      }\n      // Determine number of audio and video tracks we need to send/recv.\n      if (offerOptions) {\n        // Reject Chrome legacy constraints.\n        if (offerOptions.mandatory || offerOptions.optional) {\n          throw new TypeError(\n              'Legacy mandatory/optional constraints not supported.');\n        }\n        if (offerOptions.offerToReceiveAudio !== undefined) {\n          numAudioTracks = offerOptions.offerToReceiveAudio;\n        }\n        if (offerOptions.offerToReceiveVideo !== undefined) {\n          numVideoTracks = offerOptions.offerToReceiveVideo;\n        }\n      }\n      if (this.localStreams.length) {\n        // Push local streams.\n        this.localStreams[0].getTracks().forEach(function(track) {\n          tracks.push({\n            kind: track.kind,\n            track: track,\n            wantReceive: track.kind === 'audio' ?\n                numAudioTracks > 0 : numVideoTracks > 0\n          });\n          if (track.kind === 'audio') {\n            numAudioTracks--;\n          } else if (track.kind === 'video') {\n            numVideoTracks--;\n          }\n        });\n      }\n      // Create M-lines for recvonly streams.\n      while (numAudioTracks > 0 || numVideoTracks > 0) {\n        if (numAudioTracks > 0) {\n          tracks.push({\n            kind: 'audio',\n            wantReceive: true\n          });\n          numAudioTracks--;\n        }\n        if (numVideoTracks > 0) {\n          tracks.push({\n            kind: 'video',\n            wantReceive: true\n          });\n          numVideoTracks--;\n        }\n      }\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      var transceivers = [];\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        // For each track, create an ice gatherer, ice transport,\n        // dtls transport, potentially rtpsender and rtpreceiver.\n        var track = mline.track;\n        var kind = mline.kind;\n        var mid = SDPUtils.generateIdentifier();\n\n        var transports = self.usingBundle && sdpMLineIndex > 0 ? {\n          iceGatherer: transceivers[0].iceGatherer,\n          iceTransport: transceivers[0].iceTransport,\n          dtlsTransport: transceivers[0].dtlsTransport\n        } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);\n\n        var localCapabilities = RTCRtpSender.getCapabilities(kind);\n        var rtpSender;\n        var rtpReceiver;\n\n        // generate an ssrc now, to be used later in rtpSender.send\n        var sendEncodingParameters = [{\n          ssrc: (2 * sdpMLineIndex + 1) * 1001\n        }];\n        if (track) {\n          rtpSender = new RTCRtpSender(track, transports.dtlsTransport);\n        }\n\n        if (mline.wantReceive) {\n          rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);\n        }\n\n        transceivers[sdpMLineIndex] = {\n          iceGatherer: transports.iceGatherer,\n          iceTransport: transports.iceTransport,\n          dtlsTransport: transports.dtlsTransport,\n          localCapabilities: localCapabilities,\n          remoteCapabilities: null,\n          rtpSender: rtpSender,\n          rtpReceiver: rtpReceiver,\n          kind: kind,\n          mid: mid,\n          sendEncodingParameters: sendEncodingParameters,\n          recvEncodingParameters: null\n        };\n      });\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      tracks.forEach(function(mline, sdpMLineIndex) {\n        var transceiver = transceivers[sdpMLineIndex];\n        sdp += SDPUtils.writeMediaSection(transceiver,\n            transceiver.localCapabilities, 'offer', self.localStreams[0]);\n      });\n\n      this._pendingOffer = transceivers;\n      var desc = new RTCSessionDescription({\n        type: 'offer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.createAnswer = function() {\n      var self = this;\n\n      var sdp = SDPUtils.writeSessionBoilerplate();\n      if (this.usingBundle) {\n        sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {\n          return t.mid;\n        }).join(' ') + '\\r\\n';\n      }\n      this.transceivers.forEach(function(transceiver) {\n        // Calculate intersection of capabilities.\n        var commonCapabilities = self._getCommonCapabilities(\n            transceiver.localCapabilities,\n            transceiver.remoteCapabilities);\n\n        sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,\n            'answer', self.localStreams[0]);\n      });\n\n      var desc = new RTCSessionDescription({\n        type: 'answer',\n        sdp: sdp\n      });\n      if (arguments.length && typeof arguments[0] === 'function') {\n        window.setTimeout(arguments[0], 0, desc);\n      }\n      return Promise.resolve(desc);\n    };\n\n    window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n      if (candidate === null) {\n        this.transceivers.forEach(function(transceiver) {\n          transceiver.iceTransport.addRemoteCandidate({});\n        });\n      } else {\n        var mLineIndex = candidate.sdpMLineIndex;\n        if (candidate.sdpMid) {\n          for (var i = 0; i < this.transceivers.length; i++) {\n            if (this.transceivers[i].mid === candidate.sdpMid) {\n              mLineIndex = i;\n              break;\n            }\n          }\n        }\n        var transceiver = this.transceivers[mLineIndex];\n        if (transceiver) {\n          var cand = Object.keys(candidate.candidate).length > 0 ?\n              SDPUtils.parseCandidate(candidate.candidate) : {};\n          // Ignore Chrome's invalid candidates since Edge does not like them.\n          if (cand.protocol === 'tcp' && cand.port === 0) {\n            return;\n          }\n          // Ignore RTCP candidates, we assume RTCP-MUX.\n          if (cand.component !== '1') {\n            return;\n          }\n          // A dirty hack to make samples work.\n          if (cand.type === 'endOfCandidates') {\n            cand = {};\n          }\n          transceiver.iceTransport.addRemoteCandidate(cand);\n\n          // update the remoteDescription.\n          var sections = SDPUtils.splitSections(this.remoteDescription.sdp);\n          sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()\n              : 'a=end-of-candidates') + '\\r\\n';\n          this.remoteDescription.sdp = sections.join('');\n        }\n      }\n      if (arguments.length > 1 && typeof arguments[1] === 'function') {\n        window.setTimeout(arguments[1], 0);\n      }\n      return Promise.resolve();\n    };\n\n    window.RTCPeerConnection.prototype.getStats = function() {\n      var promises = [];\n      this.transceivers.forEach(function(transceiver) {\n        ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n            'dtlsTransport'].forEach(function(method) {\n              if (transceiver[method]) {\n                promises.push(transceiver[method].getStats());\n              }\n            });\n      });\n      var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&\n          arguments[1];\n      return new Promise(function(resolve) {\n        // shim getStats with maplike support\n        var results = new Map();\n        Promise.all(promises).then(function(res) {\n          res.forEach(function(result) {\n            Object.keys(result).forEach(function(id) {\n              results.set(id, result[id]);\n              results[id] = result[id];\n            });\n          });\n          if (cb) {\n            window.setTimeout(cb, 0, results);\n          }\n          resolve(results);\n        });\n      });\n    };\n  },\n\n  // Attach a media stream to an element.\n  attachMediaStream: function(element, stream) {\n    logging('DEPRECATED, attachMediaStream will soon be removed.');\n    element.srcObject = stream;\n  },\n\n  reattachMediaStream: function(to, from) {\n    logging('DEPRECATED, reattachMediaStream will soon be removed.');\n    to.srcObject = from.srcObject;\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimPeerConnection: edgeShim.shimPeerConnection,\n  shimGetUserMedia: require('./getusermedia'),\n  attachMediaStream: edgeShim.attachMediaStream,\n  reattachMediaStream: edgeShim.reattachMediaStream\n};\n\n},{\"../utils\":10,\"./getusermedia\":6,\"sdp\":1}],6:[function(require,module,exports){\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\n// Expose public methods.\nmodule.exports = function() {\n  var shimError_ = function(e) {\n    return {\n      name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,\n      message: e.message,\n      constraint: e.constraint,\n      toString: function() {\n        return this.name;\n      }\n    };\n  };\n\n  // getUserMedia error shim.\n  var origGetUserMedia = navigator.mediaDevices.getUserMedia.\n      bind(navigator.mediaDevices);\n  navigator.mediaDevices.getUserMedia = function(c) {\n    return origGetUserMedia(c).catch(function(e) {\n      return Promise.reject(shimError_(e));\n    });\n  };\n};\n\n},{}],7:[function(require,module,exports){\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar logging = require('../utils').log;\nvar browserDetails = require('../utils').browserDetails;\n\nvar firefoxShim = {\n  shimOnTrack: function() {\n    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in\n        window.RTCPeerConnection.prototype)) {\n      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {\n        get: function() {\n          return this._ontrack;\n        },\n        set: function(f) {\n          if (this._ontrack) {\n            this.removeEventListener('track', this._ontrack);\n            this.removeEventListener('addstream', this._ontrackpoly);\n          }\n          this.addEventListener('track', this._ontrack = f);\n          this.addEventListener('addstream', this._ontrackpoly = function(e) {\n            e.stream.getTracks().forEach(function(track) {\n              var event = new Event('track');\n              event.track = track;\n              event.receiver = {track: track};\n              event.streams = [e.stream];\n              this.dispatchEvent(event);\n            }.bind(this));\n          }.bind(this));\n        }\n      });\n    }\n  },\n\n  shimSourceObject: function() {\n    // Firefox has supported mozSrcObject since FF22, unprefixed in 42.\n    if (typeof window === 'object') {\n      if (window.HTMLMediaElement &&\n        !('srcObject' in window.HTMLMediaElement.prototype)) {\n        // Shim the srcObject property, once, when HTMLMediaElement is found.\n        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {\n          get: function() {\n            return this.mozSrcObject;\n          },\n          set: function(stream) {\n            this.mozSrcObject = stream;\n          }\n        });\n      }\n    }\n  },\n\n  shimPeerConnection: function() {\n    if (typeof window !== 'object' || !(window.RTCPeerConnection ||\n        window.mozRTCPeerConnection)) {\n      return; // probably media.peerconnection.enabled=false in about:config\n    }\n    // The RTCPeerConnection object.\n    if (!window.RTCPeerConnection) {\n      window.RTCPeerConnection = function(pcConfig, pcConstraints) {\n        if (browserDetails.version < 38) {\n          // .urls is not supported in FF < 38.\n          // create RTCIceServers with a single url.\n          if (pcConfig && pcConfig.iceServers) {\n            var newIceServers = [];\n            for (var i = 0; i < pcConfig.iceServers.length; i++) {\n              var server = pcConfig.iceServers[i];\n              if (server.hasOwnProperty('urls')) {\n                for (var j = 0; j < server.urls.length; j++) {\n                  var newServer = {\n                    url: server.urls[j]\n                  };\n                  if (server.urls[j].indexOf('turn') === 0) {\n                    newServer.username = server.username;\n                    newServer.credential = server.credential;\n                  }\n                  newIceServers.push(newServer);\n                }\n              } else {\n                newIceServers.push(pcConfig.iceServers[i]);\n              }\n            }\n            pcConfig.iceServers = newIceServers;\n          }\n        }\n        return new mozRTCPeerConnection(pcConfig, pcConstraints);\n      };\n      window.RTCPeerConnection.prototype = mozRTCPeerConnection.prototype;\n\n      // wrap static methods. Currently just generateCertificate.\n      if (mozRTCPeerConnection.generateCertificate) {\n        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {\n          get: function() {\n            return mozRTCPeerConnection.generateCertificate;\n          }\n        });\n      }\n\n      window.RTCSessionDescription = mozRTCSessionDescription;\n      window.RTCIceCandidate = mozRTCIceCandidate;\n    }\n\n    // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.\n    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n        .forEach(function(method) {\n          var nativeMethod = RTCPeerConnection.prototype[method];\n          RTCPeerConnection.prototype[method] = function() {\n            arguments[0] = new ((method === 'addIceCandidate') ?\n                RTCIceCandidate : RTCSessionDescription)(arguments[0]);\n            return nativeMethod.apply(this, arguments);\n          };\n        });\n\n    // support for addIceCandidate(null)\n    var nativeAddIceCandidate =\n        RTCPeerConnection.prototype.addIceCandidate;\n    RTCPeerConnection.prototype.addIceCandidate = function() {\n      return arguments[0] === null ? Promise.resolve()\n          : nativeAddIceCandidate.apply(this, arguments);\n    };\n\n    // shim getStats with maplike support\n    var makeMapStats = function(stats) {\n      var map = new Map();\n      Object.keys(stats).forEach(function(key) {\n        map.set(key, stats[key]);\n        map[key] = stats[key];\n      });\n      return map;\n    };\n\n    var nativeGetStats = RTCPeerConnection.prototype.getStats;\n    RTCPeerConnection.prototype.getStats = function(selector, onSucc, onErr) {\n      return nativeGetStats.apply(this, [selector || null])\n        .then(function(stats) {\n          return makeMapStats(stats);\n        })\n        .then(onSucc, onErr);\n    };\n  },\n\n  // Attach a media stream to an element.\n  attachMediaStream: function(element, stream) {\n    logging('DEPRECATED, attachMediaStream will soon be removed.');\n    element.srcObject = stream;\n  },\n\n  reattachMediaStream: function(to, from) {\n    logging('DEPRECATED, reattachMediaStream will soon be removed.');\n    to.srcObject = from.srcObject;\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimOnTrack: firefoxShim.shimOnTrack,\n  shimSourceObject: firefoxShim.shimSourceObject,\n  shimPeerConnection: firefoxShim.shimPeerConnection,\n  shimGetUserMedia: require('./getusermedia'),\n  attachMediaStream: firefoxShim.attachMediaStream,\n  reattachMediaStream: firefoxShim.reattachMediaStream\n};\n\n},{\"../utils\":10,\"./getusermedia\":8}],8:[function(require,module,exports){\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar logging = require('../utils').log;\nvar browserDetails = require('../utils').browserDetails;\n\n// Expose public methods.\nmodule.exports = function() {\n  var shimError_ = function(e) {\n    return {\n      name: {\n        SecurityError: 'NotAllowedError',\n        PermissionDeniedError: 'NotAllowedError'\n      }[e.name] || e.name,\n      message: {\n        'The operation is insecure.': 'The request is not allowed by the ' +\n        'user agent or the platform in the current context.'\n      }[e.message] || e.message,\n      constraint: e.constraint,\n      toString: function() {\n        return this.name + (this.message && ': ') + this.message;\n      }\n    };\n  };\n\n  // getUserMedia constraints shim.\n  var getUserMedia_ = function(constraints, onSuccess, onError) {\n    var constraintsToFF37_ = function(c) {\n      if (typeof c !== 'object' || c.require) {\n        return c;\n      }\n      var require = [];\n      Object.keys(c).forEach(function(key) {\n        if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n          return;\n        }\n        var r = c[key] = (typeof c[key] === 'object') ?\n            c[key] : {ideal: c[key]};\n        if (r.min !== undefined ||\n            r.max !== undefined || r.exact !== undefined) {\n          require.push(key);\n        }\n        if (r.exact !== undefined) {\n          if (typeof r.exact === 'number') {\n            r. min = r.max = r.exact;\n          } else {\n            c[key] = r.exact;\n          }\n          delete r.exact;\n        }\n        if (r.ideal !== undefined) {\n          c.advanced = c.advanced || [];\n          var oc = {};\n          if (typeof r.ideal === 'number') {\n            oc[key] = {min: r.ideal, max: r.ideal};\n          } else {\n            oc[key] = r.ideal;\n          }\n          c.advanced.push(oc);\n          delete r.ideal;\n          if (!Object.keys(r).length) {\n            delete c[key];\n          }\n        }\n      });\n      if (require.length) {\n        c.require = require;\n      }\n      return c;\n    };\n    constraints = JSON.parse(JSON.stringify(constraints));\n    if (browserDetails.version < 38) {\n      logging('spec: ' + JSON.stringify(constraints));\n      if (constraints.audio) {\n        constraints.audio = constraintsToFF37_(constraints.audio);\n      }\n      if (constraints.video) {\n        constraints.video = constraintsToFF37_(constraints.video);\n      }\n      logging('ff37: ' + JSON.stringify(constraints));\n    }\n    return navigator.mozGetUserMedia(constraints, onSuccess, function(e) {\n      onError(shimError_(e));\n    });\n  };\n\n  // Returns the result of getUserMedia as a Promise.\n  var getUserMediaPromise_ = function(constraints) {\n    return new Promise(function(resolve, reject) {\n      getUserMedia_(constraints, resolve, reject);\n    });\n  };\n\n  // Shim for mediaDevices on older versions.\n  if (!navigator.mediaDevices) {\n    navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,\n      addEventListener: function() { },\n      removeEventListener: function() { }\n    };\n  }\n  navigator.mediaDevices.enumerateDevices =\n      navigator.mediaDevices.enumerateDevices || function() {\n        return new Promise(function(resolve) {\n          var infos = [\n            {kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},\n            {kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}\n          ];\n          resolve(infos);\n        });\n      };\n\n  if (browserDetails.version < 41) {\n    // Work around http://bugzil.la/1169665\n    var orgEnumerateDevices =\n        navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);\n    navigator.mediaDevices.enumerateDevices = function() {\n      return orgEnumerateDevices().then(undefined, function(e) {\n        if (e.name === 'NotFoundError') {\n          return [];\n        }\n        throw e;\n      });\n    };\n  }\n  if (browserDetails.version < 49) {\n    var origGetUserMedia = navigator.mediaDevices.getUserMedia.\n        bind(navigator.mediaDevices);\n    navigator.mediaDevices.getUserMedia = function(c) {\n      return origGetUserMedia(c).catch(function(e) {\n        return Promise.reject(shimError_(e));\n      });\n    };\n  }\n  navigator.getUserMedia = function(constraints, onSuccess, onError) {\n    if (browserDetails.version < 44) {\n      return getUserMedia_(constraints, onSuccess, onError);\n    }\n    // Replace Firefox 44+'s deprecation warning with unprefixed version.\n    console.warn('navigator.getUserMedia has been replaced by ' +\n                 'navigator.mediaDevices.getUserMedia');\n    navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);\n  };\n};\n\n},{\"../utils\":10}],9:[function(require,module,exports){\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n'use strict';\nvar safariShim = {\n  // TODO: DrAlex, should be here, double check against LayoutTests\n  // shimOnTrack: function() { },\n\n  // TODO: DrAlex\n  // attachMediaStream: function(element, stream) { },\n  // reattachMediaStream: function(to, from) { },\n\n  // TODO: once the back-end for the mac port is done, add.\n  // TODO: check for webkitGTK+\n  // shimPeerConnection: function() { },\n\n  shimGetUserMedia: function() {\n    navigator.getUserMedia = navigator.webkitGetUserMedia;\n  }\n};\n\n// Expose public methods.\nmodule.exports = {\n  shimGetUserMedia: safariShim.shimGetUserMedia\n  // TODO\n  // shimOnTrack: safariShim.shimOnTrack,\n  // shimPeerConnection: safariShim.shimPeerConnection,\n  // attachMediaStream: safariShim.attachMediaStream,\n  // reattachMediaStream: safariShim.reattachMediaStream\n};\n\n},{}],10:[function(require,module,exports){\n/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar logDisabled_ = true;\n\n// Utility methods.\nvar utils = {\n  disableLog: function(bool) {\n    if (typeof bool !== 'boolean') {\n      return new Error('Argument type: ' + typeof bool +\n          '. Please use a boolean.');\n    }\n    logDisabled_ = bool;\n    return (bool) ? 'adapter.js logging disabled' :\n        'adapter.js logging enabled';\n  },\n\n  log: function() {\n    if (typeof window === 'object') {\n      if (logDisabled_) {\n        return;\n      }\n      if (typeof console !== 'undefined' && typeof console.log === 'function') {\n        console.log.apply(console, arguments);\n      }\n    }\n  },\n\n  /**\n   * Extract browser version out of the provided user agent string.\n   *\n   * @param {!string} uastring userAgent string.\n   * @param {!string} expr Regular expression used as match criteria.\n   * @param {!number} pos position in the version string to be returned.\n   * @return {!number} browser version.\n   */\n  extractVersion: function(uastring, expr, pos) {\n    var match = uastring.match(expr);\n    return match && match.length >= pos && parseInt(match[pos], 10);\n  },\n\n  /**\n   * Browser detector.\n   *\n   * @return {object} result containing browser, version and minVersion\n   *     properties.\n   */\n  detectBrowser: function() {\n    // Returned result object.\n    var result = {};\n    result.browser = null;\n    result.version = null;\n    result.minVersion = null;\n\n    // Fail early if it's not a browser\n    if (typeof window === 'undefined' || !window.navigator) {\n      result.browser = 'Not a browser.';\n      return result;\n    }\n\n    // Firefox.\n    if (navigator.mozGetUserMedia) {\n      result.browser = 'firefox';\n      result.version = this.extractVersion(navigator.userAgent,\n          /Firefox\\/([0-9]+)\\./, 1);\n      result.minVersion = 31;\n\n    // all webkit-based browsers\n    } else if (navigator.webkitGetUserMedia) {\n      // Chrome, Chromium, Webview, Opera, all use the chrome shim for now\n      if (window.webkitRTCPeerConnection) {\n        result.browser = 'chrome';\n        result.version = this.extractVersion(navigator.userAgent,\n          /Chrom(e|ium)\\/([0-9]+)\\./, 2);\n        result.minVersion = 38;\n\n      // Safari or unknown webkit-based\n      // for the time being Safari has support for MediaStreams but not webRTC\n      } else {\n        // Safari UA substrings of interest for reference:\n        // - webkit version:           AppleWebKit/602.1.25 (also used in Op,Cr)\n        // - safari UI version:        Version/9.0.3 (unique to Safari)\n        // - safari UI webkit version: Safari/601.4.4 (also used in Op,Cr)\n        //\n        // if the webkit version and safari UI webkit versions are equals,\n        // ... this is a stable version.\n        //\n        // only the internal webkit version is important today to know if\n        // media streams are supported\n        //\n        if (navigator.userAgent.match(/Version\\/(\\d+).(\\d+)/)) {\n          result.browser = 'safari';\n          result.version = this.extractVersion(navigator.userAgent,\n            /AppleWebKit\\/([0-9]+)\\./, 1);\n          result.minVersion = 602;\n\n        // unknown webkit-based browser\n        } else {\n          result.browser = 'Unsupported webkit-based browser ' +\n              'with GUM support but no WebRTC support.';\n          return result;\n        }\n      }\n\n    // Edge.\n    } else if (navigator.mediaDevices &&\n        navigator.userAgent.match(/Edge\\/(\\d+).(\\d+)$/)) {\n      result.browser = 'edge';\n      result.version = this.extractVersion(navigator.userAgent,\n          /Edge\\/(\\d+).(\\d+)$/, 2);\n      result.minVersion = 10547;\n\n    // Default fallthrough: not supported.\n    } else {\n      result.browser = 'Not a supported browser.';\n      return result;\n    }\n\n    // Warn if version is less than minVersion.\n    if (result.version < result.minVersion) {\n      utils.log('Browser: ' + result.browser + ' Version: ' + result.version +\n          ' < minimum supported version: ' + result.minVersion +\n          '\\n some things might not work!');\n    }\n\n    return result;\n  }\n};\n\n// Export.\nmodule.exports = {\n  log: utils.log,\n  disableLog: utils.disableLog,\n  browserDetails: utils.detectBrowser(),\n  extractVersion: utils.extractVersion\n};\n\n},{}]},{},[2])(2)\n});"
  },
  {
    "path": "examples/static/demo.css",
    "content": "\n#logs_container {\n  display: flex;\n  flex-direction: row;\n  width: 100%;\n}\n\n\n#reliable_logs {\n  flex: 1;\n}\n\n#datachannel_logs {\n  flex: 1;\n}\n"
  },
  {
    "path": "examples/static/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>WebRTC DataChannels Demo Site</title>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/demo.css\">\n  <script src=\"/static/jquery-3.0.0.min.js\"></script>\n  <script src=\"/static/adapter.js\"></script>\n  <script>\n\n  // Global vars\n  var dataChannelLabel = \"testchannel\";\n  var reliableSocket = null;\n  var peerConnection = null;\n  var dataChannel = null;\n  var remoteCandidates = [];\n  var have_answer = false;\n\n  function reliable_log_msg(msg) {\n    console.log(msg);\n    $(\"#reliable_log_list\").prepend(\"<li>\" + msg + \"</li>\");\n  }\n\n  function datachannel_log_msg(msg) {\n    console.log(msg);\n    $(\"#datachannel_log_list\").prepend(\"<li>\" + msg + \"</li>\");\n  }\n\n    $(document).ready(function () {\n\n      var sourceBuffer = null;\n      var video = document.querySelector('video');\n      var mimeCodec = 'video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"';\n\n      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {\n        var mediaSource = new MediaSource;\n        video.src = URL.createObjectURL(mediaSource);\n        mediaSource.addEventListener('sourceopen', function () {\n          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);\n          sourceBuffer.addEventListener('updateend', function () {\n            video.play();\n          });\n        });\n      } else {\n        console.error(\"Unsupported MIME type or codec: \", mimeCodec);\n      }\n\n      /**\n       * Add the various callback handlers to the PeerConnection.\n       * Shared between both clients.\n       */\n      var setupPeerConnection = function () {\n        peerConnection = new RTCPeerConnection({\n          iceServers: [{\n              urls: [\n                \"stun:stun.l.google.com:19302\",\n                \"stun:stun1.l.google.com:19302\",\n                \"stun:stun2.l.google.com:19302\",\n                \"stun:stun3.l.google.com:19302\",\n                \"stun:stun4.l.google.com:19302\"\n        ]}]});\n\n        peerConnection.onicecandidate = function (event) {\n          if (event.candidate) {\n            reliableSocket.sendMessage(\"candidate\", event.candidate);\n          } else {\n            datachannel_log_msg(\"All local candidates received\");\n          }\n        };\n\n        peerConnection.ondatachannel = function (event) {\n          if (event.channel.label == dataChannelLabel) {\n            dataChannel = event.channel;\n            datachannel_log_msg(\"DataChannel received\");\n            setupDataChannel(event.channel);\n          } else {\n            datachannel_log_msg(\"Unknown CataChannel label: \" + event.channel.label);\n          }\n        }\n      };\n\n      /**\n       * Add the various callback handlers to the DataChannel.\n       * Shared between both clients.\n       */\n      var setupDataChannel = function (dataChannel) {\n        dataChannel.onopen = function (e) {\n          datachannel_log_msg(\"DataChannel open and ready to be used\");\n\n          $(\"#send_datachannel_msg\").click(function () {\n            var msg = $(\"#datachannel_msg\").val();\n            datachannel_log_msg(\"Sending message: \" + msg);\n            dataChannel.send(msg);\n          });\n        };\n\n        dataChannel.onclose = function () {\n          datachannel_log_msg(\"DataChannel closed\");\n        };\n\n        dataChannel.onerror = function (e) {\n          datachannel_log_msg(\"DataChannel error: \" + e.message);\n          console.log(e);\n        };\n\n        dataChannel.onmessage = function (e) {\n          datachannel_log_msg(\"Received message: \" + e.data);\n          if (sourceBuffer != null) {\n            sourceBuffer.appendBuffer(e.data);\n          } else {\n            console.log(\"Got data but sourceBuffer is null\");\n          }\n        };\n      };\n\n      var createOffer = function () {\n        setupPeerConnection();\n        dataChannel = peerConnection.createDataChannel(\"testchannel\");\n        setupDataChannel(dataChannel);\n\n        peerConnection.createOffer().then(function(offer) {\n          return peerConnection.setLocalDescription(offer);\n        })\n        .then(function() {\n          reliableSocket.sendMessage(\"offer\", peerConnection.localDescription);\n        })\n        .catch(function(reason) {\n          // An error occurred, so handle the failure to connect\n          console.log(\"RTC Error\", reason);\n        });\n      };\n\n      var createAnswer = function (msg) {\n        setupPeerConnection();\n\n        var desc = new RTCSessionDescription(msg);\n\n        peerConnection.setRemoteDescription(desc)\n        .then(function () {\n          return peerConnection.createAnswer();\n        })\n        .then(function(answer) {\n          return peerConnection.setLocalDescription(answer);\n        })\n        .then(function() {\n          reliableSocket.sendMessage(\"answer\", peerConnection.localDescription);\n        })\n        .catch(function () {\n          console.log(\"RTC Error\", reason);\n        });\n      };\n\n      var handleCandidate = function (msg) {\n        var candidate = new RTCIceCandidate(msg);\n        peerConnection.addIceCandidate(candidate).then(function () {\n          datachannel_log_msg(\"New remote candidate received\");\n        }).catch(function (e) {\n          console.log(\"Error: Failure during addIceCandidate()\", e);\n        });\n      }\n\n      $(\"#connect_channel\").on('click', function (e) {\n        var channel_name = $(\"#channel_name\").val();\n        var wsAddress = \"ws://\" + window.location.host + \"/channel/\" + channel_name;\n        console.log(\"Attempting WebSocket connection to \" + wsAddress);\n\n        reliableSocket = new WebSocket(wsAddress);\n\n        reliableSocket.onopen = function (event) {\n          // Socket is now ready to send and receive messages\n          console.log(\"reliableSocket is open and ready to use\");\n          reliableSocket.sendMessage(\"client_connected\", {});\n        };\n\n        reliableSocket.onerror = function (event) {\n          // Socket failed to connect\n        };\n\n        reliableSocket.onclose = function (event) {\n          console.log(\"ERROR: Reliable socket has closed\");\n        };\n\n        // Simple helper to send JSON messages with a given type\n        reliableSocket.sendMessage = function (type, msg) {\n          reliable_log_msg(\"Sending msg of type: \" + type);\n          reliableSocket.send(JSON.stringify({\"type\": type, \"msg\": msg}));\n        }\n\n        reliableSocket.onmessage = function (event) {\n          console.log(\"Got msg\", event);\n          var msg = JSON.parse(event.data);\n\n          reliable_log_msg(\"Received msg of type: \" + msg.type);\n          console.log(msg);\n\n          switch (msg.type) {\n            case \"client_connected\":\n              reliable_log_msg(\"Client connected: starting RTC handshake\");\n              createOffer();\n              break;\n            case \"client_disconnected\":\n              reliable_log_msg(\"Remote client disconnected\");\n              break;\n            case \"offer\":\n              createAnswer(msg.msg);\n              break;\n            case \"answer\":\n              peerConnection.setRemoteDescription(new RTCSessionDescription(msg.msg))\n              .then(function () {\n                have_answer = true;\n                var i = 0;\n                for (i = 0; i < remoteCandidates.length; i++) {\n                  handleCandidate(remoteCandidates[i]);\n                }\n              });\n              break;\n            case \"candidate\":\n              if (msg.msg.candidate) {\n                if (!have_answer) {\n                  remoteCandidates.push(msg.msg);\n                } else {\n                  handleCandidate(msg.msg);\n                }\n              } else {\n                console.log(\"Remote peer has no more candidates\");\n              }\n              break;\n            default:\n              console.log(\"WARNING: Ignoring unknown msg of type '\" + msg.type + \"'\");\n              break;\n          }\n        };\n\n\n      });\n\n    });\n\n  </script>\n</head>\n<body>\n  <video controls></video>\n  <h1>WebRTC DataChannels Demo</h1>\n\n  <div id=\"channel_select\">\n    <label for=\"channel_name\">Enter a channel name:</label>\n    <input id=\"channel_name\" type=\"text\"></input>\n    <button id=\"connect_channel\">Connect</button>\n  </div>\n\n  <div id=\"logs_container\">\n    <div id=\"reliable_logs\">\n      <h2>WebSocket Logs</h2>\n      <ul id=\"reliable_log_list\">\n      </ul>\n    </div>\n    <div id=\"datachannel_logs\">\n      <h2>DataChannel Logs</h2>\n      <label for=\"datachannel_msg\">Send Msg:</label>\n      <input id=\"datachannel_msg\" type=\"text\"></input>\n      <button id=\"send_datachannel_msg\">Send</button>\n      <ul id=\"datachannel_log_list\">\n      </ul>\n    </div>\n  </div>\n\n</body>\n</html>\n"
  },
  {
    "path": "examples/websocket_client/CMakeLists.txt",
    "content": "add_executable(testclient\n        json/json.h\n        json/json-forwards.h\n        easywsclient.cpp\n        easywsclient.hpp\n        jsoncpp.cpp\n        testclient.cpp\n        WebSocketWrapper.cpp\n        WebSocketWrapper.hpp)\n\ntarget_link_libraries(testclient rtcdcpp)\n"
  },
  {
    "path": "examples/websocket_client/WebSocketWrapper.cpp",
    "content": "#include \"WebSocketWrapper.hpp\"\n#include <thread>\n#include <iostream>\n\nusing namespace rtcdcpp;\n\nWebSocketWrapper::WebSocketWrapper(std::string url) : url(url), send_queue() { ; }\n\nWebSocketWrapper::~WebSocketWrapper() { delete this->ws; }\n\nbool WebSocketWrapper::Initialize() {\n  this->ws = WebSocket::from_url(this->url);\n  return this->ws ? true : false;\n}\n\nvoid WebSocketWrapper::SetOnMessage(std::function<void(std::string)> onMessage) { this->onMessage = onMessage; }\n\nvoid WebSocketWrapper::Start() { \n  this->stopping = false;\n  this->send_loop = std::thread(&WebSocketWrapper::Loop, this); \n}\n\nvoid WebSocketWrapper::Loop() {\n  while (!this->stopping) {\n    this->ws->poll();\n    std::this_thread::sleep_for(std::chrono::milliseconds(50));\n    if (!this->send_queue.empty()) {\n      ChunkPtr chunk = this->send_queue.wait_and_pop();\n      std::string msg(reinterpret_cast<char const*>(chunk->Data()), chunk->Length());\n      this->ws->send(msg);\n      this->ws->poll();\n    }\n    this->ws->dispatch(this->onMessage);\n  }\n}\n\nvoid WebSocketWrapper::Send(std::string msg) { this->send_queue.push(std::shared_ptr<Chunk>(new Chunk((const void*)msg.c_str(), msg.length()))); }\n\nvoid WebSocketWrapper::Close() { this->stopping = true; this->send_loop.join(); }\n"
  },
  {
    "path": "examples/websocket_client/WebSocketWrapper.hpp",
    "content": "/**\n * Simple libwebsockets C++ wrapper\n */\n\n#include \"easywsclient.hpp\"\n\n#include <assert.h>\n#include <stdio.h>\n\n#include <functional>\n#include <memory>\n#include <mutex>\n#include <string>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include <rtcdcpp/ChunkQueue.hpp>\n\nusing easywsclient::WebSocket;\n\nclass WebSocketWrapper {\n public:\n  WebSocketWrapper(std::string url);\n  virtual ~WebSocketWrapper();\n\n  bool Initialize();\n  void Start();\n  void Send(std::string);\n  void Close();\n\n  void SetOnMessage(std::function<void(std::string)>);\n  void SetOnClose(std::function<void()>);\n  void SetOnError(std::function<void(std::string)>);\n\n private:\n  void Loop();\n  bool stopping;\n  WebSocket::pointer ws;\n  std::string url;\n  rtcdcpp::ChunkQueue send_queue;\n  std::function<void(std::string)> onMessage;\n  std::thread send_loop;\n};\n"
  },
  {
    "path": "examples/websocket_client/easywsclient.cpp",
    "content": "\n#ifdef _WIN32\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS  // _CRT_SECURE_NO_WARNINGS for sscanf errors in MSVC2013 Express\n#endif\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <WS2tcpip.h>\n#include <WinSock2.h>\n#include <fcntl.h>\n#pragma comment(lib, \"ws2_32\")\n#include <io.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#ifndef _SSIZE_T_DEFINED\ntypedef int ssize_t;\n#define _SSIZE_T_DEFINED\n#endif\n#ifndef _SOCKET_T_DEFINED\ntypedef SOCKET socket_t;\n#define _SOCKET_T_DEFINED\n#endif\n#ifndef snprintf\n#define snprintf _snprintf_s\n#endif\n#if _MSC_VER >= 1600\n// vs2010 or later\n#include <stdint.h>\n#else\ntypedef __int8 int8_t;\ntypedef unsigned __int8 uint8_t;\ntypedef __int32 int32_t;\ntypedef unsigned __int32 uint32_t;\ntypedef __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n#endif\n#define socketerrno WSAGetLastError()\n#define SOCKET_EAGAIN_EINPROGRESS WSAEINPROGRESS\n#define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK\n#else\n#include <fcntl.h>\n#include <netdb.h>\n#include <netinet/tcp.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n#ifndef _SOCKET_T_DEFINED\ntypedef int socket_t;\n#define _SOCKET_T_DEFINED\n#endif\n#ifndef INVALID_SOCKET\n#define INVALID_SOCKET (-1)\n#endif\n#ifndef SOCKET_ERROR\n#define SOCKET_ERROR (-1)\n#endif\n#define closesocket(s) ::close(s)\n#include <errno.h>\n#define socketerrno errno\n#define SOCKET_EAGAIN_EINPROGRESS EAGAIN\n#define SOCKET_EWOULDBLOCK EWOULDBLOCK\n#endif\n\n#include <string>\n#include <vector>\n\n#include \"easywsclient.hpp\"\n\nusing easywsclient::Callback_Imp;\nusing easywsclient::BytesCallback_Imp;\n\nnamespace {  // private module-only namespace\n\nsocket_t hostname_connect(const std::string& hostname, int port) {\n  struct addrinfo hints;\n  struct addrinfo* result;\n  struct addrinfo* p;\n  int ret;\n  socket_t sockfd = INVALID_SOCKET;\n  char sport[16];\n  memset(&hints, 0, sizeof(hints));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  snprintf(sport, 16, \"%d\", port);\n  if ((ret = getaddrinfo(hostname.c_str(), sport, &hints, &result)) != 0) {\n    fprintf(stderr, \"getaddrinfo: %s\\n\", gai_strerror(ret));\n    return 1;\n  }\n  for (p = result; p != NULL; p = p->ai_next) {\n    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);\n    if (sockfd == INVALID_SOCKET) {\n      continue;\n    }\n    if (connect(sockfd, p->ai_addr, p->ai_addrlen) != SOCKET_ERROR) {\n      break;\n    }\n    closesocket(sockfd);\n    sockfd = INVALID_SOCKET;\n  }\n  freeaddrinfo(result);\n  return sockfd;\n}\n\nclass _DummyWebSocket : public easywsclient::WebSocket {\n public:\n  void poll(int timeout) {}\n  void send(const std::string& message) {}\n  void sendBinary(const std::string& message) {}\n  void sendBinary(const std::vector<uint8_t>& message) {}\n  void sendPing() {}\n  void close() {}\n  readyStateValues getReadyState() const { return CLOSED; }\n  void _dispatch(Callback_Imp& callable) {}\n  void _dispatchBinary(BytesCallback_Imp& callable) {}\n};\n\nclass _RealWebSocket : public easywsclient::WebSocket {\n public:\n  // http://tools.ietf.org/html/rfc6455#section-5.2  Base Framing Protocol\n  //\n  //  0                   1                   2                   3\n  //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n  // +-+-+-+-+-------+-+-------------+-------------------------------+\n  // |F|R|R|R| opcode|M| Payload len |    Extended payload length    |\n  // |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |\n  // |N|V|V|V|       |S|             |   (if payload len==126/127)   |\n  // | |1|2|3|       |K|             |                               |\n  // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +\n  // |     Extended payload length continued, if payload len == 127  |\n  // + - - - - - - - - - - - - - - - +-------------------------------+\n  // |                               |Masking-key, if MASK set to 1  |\n  // +-------------------------------+-------------------------------+\n  // | Masking-key (continued)       |          Payload Data         |\n  // +-------------------------------- - - - - - - - - - - - - - - - +\n  // :                     Payload Data continued ...                :\n  // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +\n  // |                     Payload Data continued ...                |\n  // +---------------------------------------------------------------+\n  struct wsheader_type {\n    unsigned header_size;\n    bool fin;\n    bool mask;\n    enum opcode_type {\n      CONTINUATION = 0x0,\n      TEXT_FRAME = 0x1,\n      BINARY_FRAME = 0x2,\n      CLOSE = 8,\n      PING = 9,\n      PONG = 0xa,\n    } opcode;\n    int N0;\n    uint64_t N;\n    uint8_t masking_key[4];\n  };\n\n  std::vector<uint8_t> rxbuf;\n  std::vector<uint8_t> txbuf;\n  std::vector<uint8_t> receivedData;\n\n  socket_t sockfd;\n  readyStateValues readyState;\n  bool useMask;\n\n  _RealWebSocket(socket_t sockfd, bool useMask) : sockfd(sockfd), readyState(OPEN), useMask(useMask) {}\n\n  readyStateValues getReadyState() const { return readyState; }\n\n  void poll(int timeout) {  // timeout in milliseconds\n    if (readyState == CLOSED) {\n      if (timeout > 0) {\n        timeval tv = {timeout / 1000, (timeout % 1000) * 1000};\n        select(0, NULL, NULL, NULL, &tv);\n      }\n      return;\n    }\n    if (timeout != 0) {\n      fd_set rfds;\n      fd_set wfds;\n      timeval tv = {timeout / 1000, (timeout % 1000) * 1000};\n      FD_ZERO(&rfds);\n      FD_ZERO(&wfds);\n      FD_SET(sockfd, &rfds);\n      if (txbuf.size()) {\n        FD_SET(sockfd, &wfds);\n      }\n      select(sockfd + 1, &rfds, &wfds, 0, timeout > 0 ? &tv : 0);\n    }\n    while (true) {\n      // FD_ISSET(0, &rfds) will be true\n      int N = rxbuf.size();\n      ssize_t ret;\n      rxbuf.resize(N + 1500);\n      ret = recv(sockfd, (char*)&rxbuf[0] + N, 1500, 0);\n      if (false) {\n      } else if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) {\n        rxbuf.resize(N);\n        break;\n      } else if (ret <= 0) {\n        rxbuf.resize(N);\n        closesocket(sockfd);\n        readyState = CLOSED;\n        fputs(ret < 0 ? \"Connection error!\\n\" : \"Connection closed!\\n\", stderr);\n        break;\n      } else {\n        rxbuf.resize(N + ret);\n      }\n    }\n    while (txbuf.size()) {\n      int ret = ::send(sockfd, (char*)&txbuf[0], txbuf.size(), 0);\n      if (false) {\n      }  // ??\n      else if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) {\n        break;\n      } else if (ret <= 0) {\n        closesocket(sockfd);\n        readyState = CLOSED;\n        fputs(ret < 0 ? \"Connection error!\\n\" : \"Connection closed!\\n\", stderr);\n        break;\n      } else {\n        txbuf.erase(txbuf.begin(), txbuf.begin() + ret);\n      }\n    }\n    if (!txbuf.size() && readyState == CLOSING) {\n      closesocket(sockfd);\n      readyState = CLOSED;\n    }\n  }\n\n  // Callable must have signature: void(const std::string & message).\n  // Should work with C functions, C++ functors, and C++11 std::function and\n  // lambda:\n  // template<class Callable>\n  // void dispatch(Callable callable)\n  virtual void _dispatch(Callback_Imp& callable) {\n    struct CallbackAdapter : public BytesCallback_Imp\n    // Adapt void(const std::string<uint8_t>&) to void(const std::string&)\n    {\n      Callback_Imp& callable;\n      CallbackAdapter(Callback_Imp& callable) : callable(callable) {}\n      void operator()(const std::vector<uint8_t>& message) {\n        std::string stringMessage(message.begin(), message.end());\n        callable(stringMessage);\n      }\n    };\n    CallbackAdapter bytesCallback(callable);\n    _dispatchBinary(bytesCallback);\n  }\n\n  virtual void _dispatchBinary(BytesCallback_Imp& callable) {\n    // TODO: consider acquiring a lock on rxbuf...\n    while (true) {\n      wsheader_type ws;\n      if (rxbuf.size() < 2) {\n        return; /* Need at least 2 */\n      }\n      const uint8_t* data = (uint8_t*)&rxbuf[0];  // peek, but don't consume\n      ws.fin = (data[0] & 0x80) == 0x80;\n      ws.opcode = (wsheader_type::opcode_type)(data[0] & 0x0f);\n      ws.mask = (data[1] & 0x80) == 0x80;\n      ws.N0 = (data[1] & 0x7f);\n      ws.header_size = 2 + (ws.N0 == 126 ? 2 : 0) + (ws.N0 == 127 ? 8 : 0) + (ws.mask ? 4 : 0);\n      if (rxbuf.size() < ws.header_size) {\n        return; /* Need: ws.header_size - rxbuf.size() */\n      }\n      int i = 0;\n      if (ws.N0 < 126) {\n        ws.N = ws.N0;\n        i = 2;\n      } else if (ws.N0 == 126) {\n        ws.N = 0;\n        ws.N |= ((uint64_t)data[2]) << 8;\n        ws.N |= ((uint64_t)data[3]) << 0;\n        i = 4;\n      } else if (ws.N0 == 127) {\n        ws.N = 0;\n        ws.N |= ((uint64_t)data[2]) << 56;\n        ws.N |= ((uint64_t)data[3]) << 48;\n        ws.N |= ((uint64_t)data[4]) << 40;\n        ws.N |= ((uint64_t)data[5]) << 32;\n        ws.N |= ((uint64_t)data[6]) << 24;\n        ws.N |= ((uint64_t)data[7]) << 16;\n        ws.N |= ((uint64_t)data[8]) << 8;\n        ws.N |= ((uint64_t)data[9]) << 0;\n        i = 10;\n      }\n      if (ws.mask) {\n        ws.masking_key[0] = ((uint8_t)data[i + 0]) << 0;\n        ws.masking_key[1] = ((uint8_t)data[i + 1]) << 0;\n        ws.masking_key[2] = ((uint8_t)data[i + 2]) << 0;\n        ws.masking_key[3] = ((uint8_t)data[i + 3]) << 0;\n      } else {\n        ws.masking_key[0] = 0;\n        ws.masking_key[1] = 0;\n        ws.masking_key[2] = 0;\n        ws.masking_key[3] = 0;\n      }\n      if (rxbuf.size() < ws.header_size + ws.N) {\n        return; /* Need: ws.header_size+ws.N - rxbuf.size() */\n      }\n\n      // We got a whole message, now do something with it:\n      if (false) {\n      } else if (ws.opcode == wsheader_type::TEXT_FRAME || ws.opcode == wsheader_type::BINARY_FRAME || ws.opcode == wsheader_type::CONTINUATION) {\n        if (ws.mask) {\n          for (size_t i = 0; i != ws.N; ++i) {\n            rxbuf[i + ws.header_size] ^= ws.masking_key[i & 0x3];\n          }\n        }\n        receivedData.insert(receivedData.end(), rxbuf.begin() + ws.header_size, rxbuf.begin() + ws.header_size + (size_t)ws.N);  // just feed\n        if (ws.fin) {\n          callable((const std::vector<uint8_t>)receivedData);\n          receivedData.erase(receivedData.begin(), receivedData.end());\n          std::vector<uint8_t>().swap(receivedData);  // free memory\n        }\n      } else if (ws.opcode == wsheader_type::PING) {\n        if (ws.mask) {\n          for (size_t i = 0; i != ws.N; ++i) {\n            rxbuf[i + ws.header_size] ^= ws.masking_key[i & 0x3];\n          }\n        }\n        std::string data(rxbuf.begin() + ws.header_size, rxbuf.begin() + ws.header_size + (size_t)ws.N);\n        sendData(wsheader_type::PONG, data.size(), data.begin(), data.end());\n      } else if (ws.opcode == wsheader_type::PONG) {\n      } else if (ws.opcode == wsheader_type::CLOSE) {\n        close();\n      } else {\n        fprintf(stderr, \"ERROR: Got unexpected WebSocket message.\\n\");\n        close();\n      }\n\n      rxbuf.erase(rxbuf.begin(), rxbuf.begin() + ws.header_size + (size_t)ws.N);\n    }\n  }\n\n  void sendPing() {\n    std::string empty;\n    sendData(wsheader_type::PING, empty.size(), empty.begin(), empty.end());\n  }\n\n  void send(const std::string& message) { sendData(wsheader_type::TEXT_FRAME, message.size(), message.begin(), message.end()); }\n\n  void sendBinary(const std::string& message) { sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end()); }\n\n  void sendBinary(const std::vector<uint8_t>& message) { sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end()); }\n\n  template <class Iterator>\n  void sendData(wsheader_type::opcode_type type, uint64_t message_size, Iterator message_begin, Iterator message_end) {\n    // TODO:\n    // Masking key should (must) be derived from a high quality random\n    // number generator, to mitigate attacks on non-WebSocket friendly\n    // middleware:\n    const uint8_t masking_key[4] = {0x12, 0x34, 0x56, 0x78};\n    // TODO: consider acquiring a lock on txbuf...\n    if (readyState == CLOSING || readyState == CLOSED) {\n      return;\n    }\n    std::vector<uint8_t> header;\n    header.assign(2 + (message_size >= 126 ? 2 : 0) + (message_size >= 65536 ? 6 : 0) + (useMask ? 4 : 0), 0);\n    header[0] = 0x80 | type;\n    if (false) {\n    } else if (message_size < 126) {\n      header[1] = (message_size & 0xff) | (useMask ? 0x80 : 0);\n      if (useMask) {\n        header[2] = masking_key[0];\n        header[3] = masking_key[1];\n        header[4] = masking_key[2];\n        header[5] = masking_key[3];\n      }\n    } else if (message_size < 65536) {\n      header[1] = 126 | (useMask ? 0x80 : 0);\n      header[2] = (message_size >> 8) & 0xff;\n      header[3] = (message_size >> 0) & 0xff;\n      if (useMask) {\n        header[4] = masking_key[0];\n        header[5] = masking_key[1];\n        header[6] = masking_key[2];\n        header[7] = masking_key[3];\n      }\n    } else {  // TODO: run coverage testing here\n      header[1] = 127 | (useMask ? 0x80 : 0);\n      header[2] = (message_size >> 56) & 0xff;\n      header[3] = (message_size >> 48) & 0xff;\n      header[4] = (message_size >> 40) & 0xff;\n      header[5] = (message_size >> 32) & 0xff;\n      header[6] = (message_size >> 24) & 0xff;\n      header[7] = (message_size >> 16) & 0xff;\n      header[8] = (message_size >> 8) & 0xff;\n      header[9] = (message_size >> 0) & 0xff;\n      if (useMask) {\n        header[10] = masking_key[0];\n        header[11] = masking_key[1];\n        header[12] = masking_key[2];\n        header[13] = masking_key[3];\n      }\n    }\n    // N.B. - txbuf will keep growing until it can be transmitted over the socket:\n    txbuf.insert(txbuf.end(), header.begin(), header.end());\n    txbuf.insert(txbuf.end(), message_begin, message_end);\n    if (useMask) {\n      for (size_t i = 0; i != message_size; ++i) {\n        *(txbuf.end() - message_size + i) ^= masking_key[i & 0x3];\n      }\n    }\n  }\n\n  void close() {\n    if (readyState == CLOSING || readyState == CLOSED) {\n      return;\n    }\n    readyState = CLOSING;\n    uint8_t closeFrame[6] = {0x88, 0x80, 0x00, 0x00, 0x00, 0x00};  // last 4 bytes are a masking key\n    std::vector<uint8_t> header(closeFrame, closeFrame + 6);\n    txbuf.insert(txbuf.end(), header.begin(), header.end());\n  }\n};\n\neasywsclient::WebSocket::pointer from_url(const std::string& url, bool useMask, const std::string& origin) {\n  char host[128];\n  int port;\n  char path[128];\n  if (url.size() >= 128) {\n    fprintf(stderr, \"ERROR: url size limit exceeded: %s\\n\", url.c_str());\n    return NULL;\n  }\n  if (origin.size() >= 200) {\n    fprintf(stderr, \"ERROR: origin size limit exceeded: %s\\n\", origin.c_str());\n    return NULL;\n  }\n  if (false) {\n  } else if (sscanf(url.c_str(), \"ws://%[^:/]:%d/%s\", host, &port, path) == 3) {\n  } else if (sscanf(url.c_str(), \"ws://%[^:/]/%s\", host, path) == 2) {\n    port = 80;\n  } else if (sscanf(url.c_str(), \"ws://%[^:/]:%d\", host, &port) == 2) {\n    path[0] = '\\0';\n  } else if (sscanf(url.c_str(), \"ws://%[^:/]\", host) == 1) {\n    port = 80;\n    path[0] = '\\0';\n  } else {\n    fprintf(stderr, \"ERROR: Could not parse WebSocket url: %s\\n\", url.c_str());\n    return NULL;\n  }\n  fprintf(stderr, \"easywsclient: connecting: host=%s port=%d path=/%s\\n\", host, port, path);\n  socket_t sockfd = hostname_connect(host, port);\n  if (sockfd == INVALID_SOCKET) {\n    fprintf(stderr, \"Unable to connect to %s:%d\\n\", host, port);\n    return NULL;\n  }\n  {\n    // XXX: this should be done non-blocking,\n    char line[256];\n    int status;\n    int i;\n    snprintf(line, 256, \"GET /%s HTTP/1.1\\r\\n\", path);\n    ::send(sockfd, line, strlen(line), 0);\n    if (port == 80) {\n      snprintf(line, 256, \"Host: %s\\r\\n\", host);\n      ::send(sockfd, line, strlen(line), 0);\n    } else {\n      snprintf(line, 256, \"Host: %s:%d\\r\\n\", host, port);\n      ::send(sockfd, line, strlen(line), 0);\n    }\n    snprintf(line, 256, \"Upgrade: websocket\\r\\n\");\n    ::send(sockfd, line, strlen(line), 0);\n    snprintf(line, 256, \"Connection: Upgrade\\r\\n\");\n    ::send(sockfd, line, strlen(line), 0);\n    if (!origin.empty()) {\n      snprintf(line, 256, \"Origin: %s\\r\\n\", origin.c_str());\n      ::send(sockfd, line, strlen(line), 0);\n    }\n    snprintf(line, 256, \"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\\r\\n\");\n    ::send(sockfd, line, strlen(line), 0);\n    snprintf(line, 256, \"Sec-WebSocket-Version: 13\\r\\n\");\n    ::send(sockfd, line, strlen(line), 0);\n    snprintf(line, 256, \"\\r\\n\");\n    ::send(sockfd, line, strlen(line), 0);\n    for (i = 0; i < 2 || (i < 255 && line[i - 2] != '\\r' && line[i - 1] != '\\n'); ++i) {\n      if (recv(sockfd, line + i, 1, 0) == 0) {\n        return NULL;\n      }\n    }\n    line[i] = 0;\n    if (i == 255) {\n      fprintf(stderr, \"ERROR: Got invalid status line connecting to: %s\\n\", url.c_str());\n      return NULL;\n    }\n    if (sscanf(line, \"HTTP/1.1 %d\", &status) != 1 || status != 101) {\n      fprintf(stderr, \"ERROR: Got bad status connecting to %s: %s\", url.c_str(), line);\n      return NULL;\n    }\n    // TODO: verify response headers,\n    while (true) {\n      for (i = 0; i < 2 || (i < 255 && line[i - 2] != '\\r' && line[i - 1] != '\\n'); ++i) {\n        if (recv(sockfd, line + i, 1, 0) == 0) {\n          return NULL;\n        }\n      }\n      if (line[0] == '\\r' && line[1] == '\\n') {\n        break;\n      }\n    }\n  }\n  int flag = 1;\n  setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));  // Disable Nagle's algorithm\n#ifdef _WIN32\n  u_long on = 1;\n  ioctlsocket(sockfd, FIONBIO, &on);\n#else\n  fcntl(sockfd, F_SETFL, O_NONBLOCK);\n#endif\n  fprintf(stderr, \"Connected to: %s\\n\", url.c_str());\n  return easywsclient::WebSocket::pointer(new _RealWebSocket(sockfd, useMask));\n}\n\n}  // end of module-only namespace\n\nnamespace easywsclient {\n\nWebSocket::pointer WebSocket::create_dummy() {\n  static pointer dummy = pointer(new _DummyWebSocket);\n  return dummy;\n}\n\nWebSocket::pointer WebSocket::from_url(const std::string& url, const std::string& origin) { return ::from_url(url, true, origin); }\n\nWebSocket::pointer WebSocket::from_url_no_mask(const std::string& url, const std::string& origin) { return ::from_url(url, false, origin); }\n\n}  // namespace easywsclient\n"
  },
  {
    "path": "examples/websocket_client/easywsclient.hpp",
    "content": "#ifndef EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD\n#define EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD\n\n// This code comes from:\n// https://github.com/dhbaird/easywsclient\n//\n// To get the latest version:\n// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.hpp\n// wget https://raw.github.com/dhbaird/easywsclient/master/easywsclient.cpp\n\n#include <string>\n#include <vector>\n\nnamespace easywsclient {\n\nstruct Callback_Imp {\n  virtual void operator()(const std::string& message) = 0;\n};\nstruct BytesCallback_Imp {\n  virtual void operator()(const std::vector<uint8_t>& message) = 0;\n};\n\nclass WebSocket {\n public:\n  typedef WebSocket* pointer;\n  typedef enum readyStateValues { CLOSING, CLOSED, CONNECTING, OPEN } readyStateValues;\n\n  // Factories:\n  static pointer create_dummy();\n  static pointer from_url(const std::string& url, const std::string& origin = std::string());\n  static pointer from_url_no_mask(const std::string& url, const std::string& origin = std::string());\n\n  // Interfaces:\n  virtual ~WebSocket() {}\n  virtual void poll(int timeout = 0) = 0;  // timeout in milliseconds\n  virtual void send(const std::string& message) = 0;\n  virtual void sendBinary(const std::string& message) = 0;\n  virtual void sendBinary(const std::vector<uint8_t>& message) = 0;\n  virtual void sendPing() = 0;\n  virtual void close() = 0;\n  virtual readyStateValues getReadyState() const = 0;\n\n  template <class Callable>\n  void dispatch(Callable callable)\n  // For callbacks that accept a string argument.\n  {  // N.B. this is compatible with both C++11 lambdas, functors and C function pointers\n    struct _Callback : public Callback_Imp {\n      Callable& callable;\n      _Callback(Callable& callable) : callable(callable) {}\n      void operator()(const std::string& message) { callable(message); }\n    };\n    _Callback callback(callable);\n    _dispatch(callback);\n  }\n\n  template <class Callable>\n  void dispatchBinary(Callable callable)\n  // For callbacks that accept a std::vector<uint8_t> argument.\n  {  // N.B. this is compatible with both C++11 lambdas, functors and C function pointers\n    struct _Callback : public BytesCallback_Imp {\n      Callable& callable;\n      _Callback(Callable& callable) : callable(callable) {}\n      void operator()(const std::vector<uint8_t>& message) { callable(message); }\n    };\n    _Callback callback(callable);\n    _dispatchBinary(callback);\n  }\n\n protected:\n  virtual void _dispatch(Callback_Imp& callable) = 0;\n  virtual void _dispatchBinary(BytesCallback_Imp& callable) = 0;\n};\n\n}  // namespace easywsclient\n\n#endif /* EASYWSCLIENT_HPP_20120819_MIOFVASDTNUASZDQPLFD */\n"
  },
  {
    "path": "examples/websocket_client/json/json-forwards.h",
    "content": "/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).\n/// It is intended to be used with #include \"json/json-forwards.h\"\n/// This header provides forward declaration for all JsonCpp types.\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n/*\nThe JsonCpp library's source code, including accompanying documentation,\ntests and demonstration applications, are licensed under the following\nconditions...\n\nThe author (Baptiste Lepilleur) explicitly disclaims copyright in all\njurisdictions which recognize such a disclaimer. In such jurisdictions,\nthis software is released into the Public Domain.\n\nIn jurisdictions which do not recognize Public Domain property (e.g. Germany as of\n2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is\nreleased under the terms of the MIT License (see below).\n\nIn jurisdictions which recognize Public Domain property, the user of this\nsoftware may choose to accept it either as 1) Public Domain, 2) under the\nconditions of the MIT License (see below), or 3) under the terms of dual\nPublic Domain/MIT License conditions described here, as they choose.\n\nThe MIT License is about as close to Public Domain as a license can get, and is\ndescribed in clear, concise terms at:\n\n   http://en.wikipedia.org/wiki/MIT_License\n\nThe full text of the MIT License follows:\n\n========================================================================\nCopyright (c) 2007-2010 Baptiste Lepilleur\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use, copy,\nmodify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\nBE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n========================================================================\n(END LICENSE TEXT)\n\nThe MIT license is compatible with both the GPL and commercial\nsoftware, affording one all of the rights of Public Domain with the\nminor nuisance of being required to keep the above copyright notice\nand license text in the source code. Note also that by accepting the\nPublic Domain \"license\" you can re-license your copy using whatever\nlicense you like.\n\n*/\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED\n#define JSON_FORWARD_AMALGATED_H_INCLUDED\n/// If defined, indicates that the source file is amalgated\n/// to prevent private header inclusion.\n#define JSON_IS_AMALGAMATION\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/config.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef JSON_CONFIG_H_INCLUDED\n#define JSON_CONFIG_H_INCLUDED\n#include <stddef.h>\n#include <string>  //typdef String\n\n/// If defined, indicates that json library is embedded in CppTL library.\n//# define JSON_IN_CPPTL 1\n\n/// If defined, indicates that json may leverage CppTL library\n//#  define JSON_USE_CPPTL 1\n/// If defined, indicates that cpptl vector based map should be used instead of\n/// std::map\n/// as Value container.\n//#  define JSON_USE_CPPTL_SMALLMAP 1\n\n// If non-zero, the library uses exceptions to report bad input instead of C\n// assertion macros. The default is to use exceptions.\n#ifndef JSON_USE_EXCEPTION\n#define JSON_USE_EXCEPTION 1\n#endif\n\n/// If defined, indicates that the source file is amalgated\n/// to prevent private header inclusion.\n/// Remarks: it is automatically defined in the generated amalgated header.\n// #define JSON_IS_AMALGAMATION\n\n#ifdef JSON_IN_CPPTL\n#include <cpptl/config.h>\n#ifndef JSON_USE_CPPTL\n#define JSON_USE_CPPTL 1\n#endif\n#endif\n\n#ifdef JSON_IN_CPPTL\n#define JSON_API CPPTL_API\n#elif defined(JSON_DLL_BUILD)\n#if defined(_MSC_VER) || defined(__MINGW32__)\n#define JSON_API __declspec(dllexport)\n#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING\n#endif  // if defined(_MSC_VER)\n#elif defined(JSON_DLL)\n#if defined(_MSC_VER) || defined(__MINGW32__)\n#define JSON_API __declspec(dllimport)\n#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING\n#endif  // if defined(_MSC_VER)\n#endif  // ifdef JSON_IN_CPPTL\n#if !defined(JSON_API)\n#define JSON_API\n#endif\n\n// If JSON_NO_INT64 is defined, then Json only support C++ \"int\" type for\n// integer\n// Storages, and 64 bits integer support is disabled.\n// #define JSON_NO_INT64 1\n\n#if defined(_MSC_VER)  // MSVC\n#if _MSC_VER <= 1200   // MSVC 6\n                       // Microsoft Visual Studio 6 only support conversion from __int64 to double\n                       // (no conversion from unsigned __int64).\n#define JSON_USE_INT64_DOUBLE_CONVERSION 1\n// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'\n// characters in the debug information)\n// All projects I've ever seen with VS6 were using this globally (not bothering\n// with pragma push/pop).\n#pragma warning(disable : 4786)\n#endif  // MSVC 6\n\n#if _MSC_VER >= 1500  // MSVC 2008\n                      /// Indicates that the following function is deprecated.\n#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))\n#endif\n\n#endif  // defined(_MSC_VER)\n\n// In c++11 the override keyword allows you to explicity define that a function\n// is intended to override the base-class version.  This makes the code more\n// managable and fixes a set of common hard-to-find bugs.\n#if __cplusplus >= 201103L\n#define JSONCPP_OVERRIDE override\n#elif defined(_MSC_VER) && _MSC_VER > 1600\n#define JSONCPP_OVERRIDE override\n#else\n#define JSONCPP_OVERRIDE\n#endif\n\n#ifndef JSON_HAS_RVALUE_REFERENCES\n\n#if defined(_MSC_VER) && _MSC_VER >= 1600  // MSVC >= 2010\n#define JSON_HAS_RVALUE_REFERENCES 1\n#endif  // MSVC >= 2010\n\n#ifdef __clang__\n#if __has_feature(cxx_rvalue_references)\n#define JSON_HAS_RVALUE_REFERENCES 1\n#endif  // has_feature\n\n#elif defined __GNUC__  // not clang (gcc comes later since clang emulates gcc)\n#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)\n#define JSON_HAS_RVALUE_REFERENCES 1\n#endif  // GXX_EXPERIMENTAL\n\n#endif  // __clang__ || __GNUC__\n\n#endif  // not defined JSON_HAS_RVALUE_REFERENCES\n\n#ifndef JSON_HAS_RVALUE_REFERENCES\n#define JSON_HAS_RVALUE_REFERENCES 0\n#endif\n\n#ifdef __clang__\n#elif defined __GNUC__  // not clang (gcc comes later since clang emulates gcc)\n#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))\n#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))\n#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))\n#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))\n#endif  // GNUC version\n#endif  // __clang__ || __GNUC__\n\n#if !defined(JSONCPP_DEPRECATED)\n#define JSONCPP_DEPRECATED(message)\n#endif  // if !defined(JSONCPP_DEPRECATED)\n\n#if __GNUC__ >= 6\n#define JSON_USE_INT64_DOUBLE_CONVERSION 1\n#endif\n\n#if !defined(JSON_IS_AMALGAMATION)\n\n#include \"version.h\"\n\n#if JSONCPP_USING_SECURE_MEMORY\n#include \"allocator.h\"  //typedef Allocator\n#endif\n\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\ntypedef int Int;\ntypedef unsigned int UInt;\n#if defined(JSON_NO_INT64)\ntypedef int LargestInt;\ntypedef unsigned int LargestUInt;\n#undef JSON_HAS_INT64\n#else                  // if defined(JSON_NO_INT64)\n// For Microsoft Visual use specific types as long long is not supported\n#if defined(_MSC_VER)  // Microsoft Visual Studio\ntypedef __int64 Int64;\ntypedef unsigned __int64 UInt64;\n#else                  // if defined(_MSC_VER) // Other platforms, use long long\ntypedef long long int Int64;\ntypedef unsigned long long int UInt64;\n#endif                 // if defined(_MSC_VER)\ntypedef Int64 LargestInt;\ntypedef UInt64 LargestUInt;\n#define JSON_HAS_INT64\n#endif  // if defined(JSON_NO_INT64)\n#if JSONCPP_USING_SECURE_MEMORY\n#define JSONCPP_STRING std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char>>\n#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char>>\n#define JSONCPP_OSTREAM std::basic_ostream<char, std::char_traits<char>>\n#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char>>\n#define JSONCPP_ISTREAM std::istream\n#else\n#define JSONCPP_STRING std::string\n#define JSONCPP_OSTRINGSTREAM std::ostringstream\n#define JSONCPP_OSTREAM std::ostream\n#define JSONCPP_ISTRINGSTREAM std::istringstream\n#define JSONCPP_ISTREAM std::istream\n#endif  // if JSONCPP_USING_SECURE_MEMORY\n}  // end namespace Json\n\n#endif  // JSON_CONFIG_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/config.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/forwards.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef JSON_FORWARDS_H_INCLUDED\n#define JSON_FORWARDS_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"config.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\n\n// writer.h\nclass FastWriter;\nclass StyledWriter;\n\n// reader.h\nclass Reader;\n\n// features.h\nclass Features;\n\n// value.h\ntypedef unsigned int ArrayIndex;\nclass StaticString;\nclass Path;\nclass PathArgument;\nclass Value;\nclass ValueIteratorBase;\nclass ValueIterator;\nclass ValueConstIterator;\n\n}  // namespace Json\n\n#endif  // JSON_FORWARDS_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/forwards.h\n// //////////////////////////////////////////////////////////////////////\n\n#endif  // ifndef JSON_FORWARD_AMALGATED_H_INCLUDED\n"
  },
  {
    "path": "examples/websocket_client/json/json.h",
    "content": "/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).\n/// It is intended to be used with #include \"json/json.h\"\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n/*\nThe JsonCpp library's source code, including accompanying documentation,\ntests and demonstration applications, are licensed under the following\nconditions...\n\nThe author (Baptiste Lepilleur) explicitly disclaims copyright in all\njurisdictions which recognize such a disclaimer. In such jurisdictions,\nthis software is released into the Public Domain.\n\nIn jurisdictions which do not recognize Public Domain property (e.g. Germany as of\n2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is\nreleased under the terms of the MIT License (see below).\n\nIn jurisdictions which recognize Public Domain property, the user of this\nsoftware may choose to accept it either as 1) Public Domain, 2) under the\nconditions of the MIT License (see below), or 3) under the terms of dual\nPublic Domain/MIT License conditions described here, as they choose.\n\nThe MIT License is about as close to Public Domain as a license can get, and is\ndescribed in clear, concise terms at:\n\n   http://en.wikipedia.org/wiki/MIT_License\n\nThe full text of the MIT License follows:\n\n========================================================================\nCopyright (c) 2007-2010 Baptiste Lepilleur\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use, copy,\nmodify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\nBE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n========================================================================\n(END LICENSE TEXT)\n\nThe MIT license is compatible with both the GPL and commercial\nsoftware, affording one all of the rights of Public Domain with the\nminor nuisance of being required to keep the above copyright notice\nand license text in the source code. Note also that by accepting the\nPublic Domain \"license\" you can re-license your copy using whatever\nlicense you like.\n\n*/\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n#ifndef JSON_AMALGATED_H_INCLUDED\n#define JSON_AMALGATED_H_INCLUDED\n/// If defined, indicates that the source file is amalgated\n/// to prevent private header inclusion.\n#define JSON_IS_AMALGAMATION\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/version.h\n// //////////////////////////////////////////////////////////////////////\n\n// DO NOT EDIT. This file (and \"version\") is generated by CMake.\n// Run CMake configure step to update it.\n#ifndef JSON_VERSION_H_INCLUDED\n#define JSON_VERSION_H_INCLUDED\n\n#define JSONCPP_VERSION_STRING \"1.7.4\"\n#define JSONCPP_VERSION_MAJOR 1\n#define JSONCPP_VERSION_MINOR 7\n#define JSONCPP_VERSION_PATCH 4\n#define JSONCPP_VERSION_QUALIFIER\n#define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))\n\n#ifdef JSONCPP_USING_SECURE_MEMORY\n#undef JSONCPP_USING_SECURE_MEMORY\n#endif\n#define JSONCPP_USING_SECURE_MEMORY 0\n// If non-zero, the library zeroes any memory that it has allocated before\n// it frees its memory.\n\n#endif  // JSON_VERSION_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/version.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/config.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef JSON_CONFIG_H_INCLUDED\n#define JSON_CONFIG_H_INCLUDED\n#include <stddef.h>\n#include <string>  //typdef String\n\n/// If defined, indicates that json library is embedded in CppTL library.\n//# define JSON_IN_CPPTL 1\n\n/// If defined, indicates that json may leverage CppTL library\n//#  define JSON_USE_CPPTL 1\n/// If defined, indicates that cpptl vector based map should be used instead of\n/// std::map\n/// as Value container.\n//#  define JSON_USE_CPPTL_SMALLMAP 1\n\n// If non-zero, the library uses exceptions to report bad input instead of C\n// assertion macros. The default is to use exceptions.\n#ifndef JSON_USE_EXCEPTION\n#define JSON_USE_EXCEPTION 1\n#endif\n\n/// If defined, indicates that the source file is amalgated\n/// to prevent private header inclusion.\n/// Remarks: it is automatically defined in the generated amalgated header.\n// #define JSON_IS_AMALGAMATION\n\n#ifdef JSON_IN_CPPTL\n#include <cpptl/config.h>\n#ifndef JSON_USE_CPPTL\n#define JSON_USE_CPPTL 1\n#endif\n#endif\n\n#ifdef JSON_IN_CPPTL\n#define JSON_API CPPTL_API\n#elif defined(JSON_DLL_BUILD)\n#if defined(_MSC_VER) || defined(__MINGW32__)\n#define JSON_API __declspec(dllexport)\n#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING\n#endif  // if defined(_MSC_VER)\n#elif defined(JSON_DLL)\n#if defined(_MSC_VER) || defined(__MINGW32__)\n#define JSON_API __declspec(dllimport)\n#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING\n#endif  // if defined(_MSC_VER)\n#endif  // ifdef JSON_IN_CPPTL\n#if !defined(JSON_API)\n#define JSON_API\n#endif\n\n// If JSON_NO_INT64 is defined, then Json only support C++ \"int\" type for\n// integer\n// Storages, and 64 bits integer support is disabled.\n// #define JSON_NO_INT64 1\n\n#if defined(_MSC_VER)  // MSVC\n#if _MSC_VER <= 1200   // MSVC 6\n                       // Microsoft Visual Studio 6 only support conversion from __int64 to double\n                       // (no conversion from unsigned __int64).\n#define JSON_USE_INT64_DOUBLE_CONVERSION 1\n// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'\n// characters in the debug information)\n// All projects I've ever seen with VS6 were using this globally (not bothering\n// with pragma push/pop).\n#pragma warning(disable : 4786)\n#endif  // MSVC 6\n\n#if _MSC_VER >= 1500  // MSVC 2008\n                      /// Indicates that the following function is deprecated.\n#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))\n#endif\n\n#endif  // defined(_MSC_VER)\n\n// In c++11 the override keyword allows you to explicity define that a function\n// is intended to override the base-class version.  This makes the code more\n// managable and fixes a set of common hard-to-find bugs.\n#if __cplusplus >= 201103L\n#define JSONCPP_OVERRIDE override\n#elif defined(_MSC_VER) && _MSC_VER > 1600\n#define JSONCPP_OVERRIDE override\n#else\n#define JSONCPP_OVERRIDE\n#endif\n\n#ifndef JSON_HAS_RVALUE_REFERENCES\n\n#if defined(_MSC_VER) && _MSC_VER >= 1600  // MSVC >= 2010\n#define JSON_HAS_RVALUE_REFERENCES 1\n#endif  // MSVC >= 2010\n\n#ifdef __clang__\n#if __has_feature(cxx_rvalue_references)\n#define JSON_HAS_RVALUE_REFERENCES 1\n#endif  // has_feature\n\n#elif defined __GNUC__  // not clang (gcc comes later since clang emulates gcc)\n#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)\n#define JSON_HAS_RVALUE_REFERENCES 1\n#endif  // GXX_EXPERIMENTAL\n\n#endif  // __clang__ || __GNUC__\n\n#endif  // not defined JSON_HAS_RVALUE_REFERENCES\n\n#ifndef JSON_HAS_RVALUE_REFERENCES\n#define JSON_HAS_RVALUE_REFERENCES 0\n#endif\n\n#ifdef __clang__\n#elif defined __GNUC__  // not clang (gcc comes later since clang emulates gcc)\n#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))\n#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))\n#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))\n#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))\n#endif  // GNUC version\n#endif  // __clang__ || __GNUC__\n\n#if !defined(JSONCPP_DEPRECATED)\n#define JSONCPP_DEPRECATED(message)\n#endif  // if !defined(JSONCPP_DEPRECATED)\n\n#if __GNUC__ >= 6\n#define JSON_USE_INT64_DOUBLE_CONVERSION 1\n#endif\n\n#if !defined(JSON_IS_AMALGAMATION)\n\n#include \"version.h\"\n\n#if JSONCPP_USING_SECURE_MEMORY\n#include \"allocator.h\"  //typedef Allocator\n#endif\n\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\ntypedef int Int;\ntypedef unsigned int UInt;\n#if defined(JSON_NO_INT64)\ntypedef int LargestInt;\ntypedef unsigned int LargestUInt;\n#undef JSON_HAS_INT64\n#else                  // if defined(JSON_NO_INT64)\n// For Microsoft Visual use specific types as long long is not supported\n#if defined(_MSC_VER)  // Microsoft Visual Studio\ntypedef __int64 Int64;\ntypedef unsigned __int64 UInt64;\n#else                  // if defined(_MSC_VER) // Other platforms, use long long\ntypedef long long int Int64;\ntypedef unsigned long long int UInt64;\n#endif                 // if defined(_MSC_VER)\ntypedef Int64 LargestInt;\ntypedef UInt64 LargestUInt;\n#define JSON_HAS_INT64\n#endif  // if defined(JSON_NO_INT64)\n#if JSONCPP_USING_SECURE_MEMORY\n#define JSONCPP_STRING std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char>>\n#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char>>\n#define JSONCPP_OSTREAM std::basic_ostream<char, std::char_traits<char>>\n#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char>>\n#define JSONCPP_ISTREAM std::istream\n#else\n#define JSONCPP_STRING std::string\n#define JSONCPP_OSTRINGSTREAM std::ostringstream\n#define JSONCPP_OSTREAM std::ostream\n#define JSONCPP_ISTRINGSTREAM std::istringstream\n#define JSONCPP_ISTREAM std::istream\n#endif  // if JSONCPP_USING_SECURE_MEMORY\n}  // end namespace Json\n\n#endif  // JSON_CONFIG_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/config.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/forwards.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef JSON_FORWARDS_H_INCLUDED\n#define JSON_FORWARDS_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"config.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\n\n// writer.h\nclass FastWriter;\nclass StyledWriter;\n\n// reader.h\nclass Reader;\n\n// features.h\nclass Features;\n\n// value.h\ntypedef unsigned int ArrayIndex;\nclass StaticString;\nclass Path;\nclass PathArgument;\nclass Value;\nclass ValueIteratorBase;\nclass ValueIterator;\nclass ValueConstIterator;\n\n}  // namespace Json\n\n#endif  // JSON_FORWARDS_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/forwards.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/features.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef CPPTL_JSON_FEATURES_H_INCLUDED\n#define CPPTL_JSON_FEATURES_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"forwards.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\n\n/** \\brief Configuration passed to reader and writer.\n * This configuration object can be used to force the Reader or Writer\n * to behave in a standard conforming way.\n */\nclass JSON_API Features {\n public:\n  /** \\brief A configuration that allows all features and assumes all strings\n   * are UTF-8.\n   * - C & C++ comments are allowed\n   * - Root object can be any JSON value\n   * - Assumes Value strings are encoded in UTF-8\n   */\n  static Features all();\n\n  /** \\brief A configuration that is strictly compatible with the JSON\n   * specification.\n   * - Comments are forbidden.\n   * - Root object must be either an array or an object value.\n   * - Assumes Value strings are encoded in UTF-8\n   */\n  static Features strictMode();\n\n  /** \\brief Initialize the configuration like JsonConfig::allFeatures;\n   */\n  Features();\n\n  /// \\c true if comments are allowed. Default: \\c true.\n  bool allowComments_;\n\n  /// \\c true if root must be either an array or an object value. Default: \\c\n  /// false.\n  bool strictRoot_;\n\n  /// \\c true if dropped null placeholders are allowed. Default: \\c false.\n  bool allowDroppedNullPlaceholders_;\n\n  /// \\c true if numeric object key are allowed. Default: \\c false.\n  bool allowNumericKeys_;\n};\n\n}  // namespace Json\n\n#endif  // CPPTL_JSON_FEATURES_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/features.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/value.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef CPPTL_JSON_H_INCLUDED\n#define CPPTL_JSON_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"forwards.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n#include <exception>\n#include <string>\n#include <vector>\n\n#ifndef JSON_USE_CPPTL_SMALLMAP\n#include <map>\n#else\n#include <cpptl/smallmap.h>\n#endif\n#ifdef JSON_USE_CPPTL\n#include <cpptl/forwards.h>\n#endif\n\n// Conditional NORETURN attribute on the throw functions would:\n// a) suppress false positives from static code analysis\n// b) possibly improve optimization opportunities.\n#if !defined(JSONCPP_NORETURN)\n#if defined(_MSC_VER)\n#define JSONCPP_NORETURN __declspec(noreturn)\n#elif defined(__GNUC__)\n#define JSONCPP_NORETURN __attribute__((__noreturn__))\n#else\n#define JSONCPP_NORETURN\n#endif\n#endif\n\n// Disable warning C4251: <data member>: <type> needs to have dll-interface to\n// be used by...\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#endif  // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\n/** \\brief JSON (JavaScript Object Notation).\n */\nnamespace Json {\n\n/** Base class for all exceptions we throw.\n *\n * We use nothing but these internally. Of course, STL can throw others.\n */\nclass JSON_API Exception : public std::exception {\n public:\n  Exception(JSONCPP_STRING const& msg);\n  ~Exception() throw() JSONCPP_OVERRIDE;\n  char const* what() const throw() JSONCPP_OVERRIDE;\n\n protected:\n  JSONCPP_STRING msg_;\n};\n\n/** Exceptions which the user cannot easily avoid.\n *\n * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input\n *\n * \\remark derived from Json::Exception\n */\nclass JSON_API RuntimeError : public Exception {\n public:\n  RuntimeError(JSONCPP_STRING const& msg);\n};\n\n/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.\n *\n * These are precondition-violations (user bugs) and internal errors (our bugs).\n *\n * \\remark derived from Json::Exception\n */\nclass JSON_API LogicError : public Exception {\n public:\n  LogicError(JSONCPP_STRING const& msg);\n};\n\n/// used internally\nJSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg);\n/// used internally\nJSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg);\n\n/** \\brief Type of the value held by a Value object.\n */\nenum ValueType {\n  nullValue = 0,  ///< 'null' value\n  intValue,       ///< signed integer value\n  uintValue,      ///< unsigned integer value\n  realValue,      ///< double value\n  stringValue,    ///< UTF-8 string value\n  booleanValue,   ///< bool value\n  arrayValue,     ///< array value (ordered list)\n  objectValue     ///< object value (collection of name/value pairs).\n};\n\nenum CommentPlacement {\n  commentBefore = 0,       ///< a comment placed on the line before a value\n  commentAfterOnSameLine,  ///< a comment just after a value on the same line\n  commentAfter,            ///< a comment on the line after a value (only make sense for\n  /// root value)\n  numberOfCommentPlacement\n};\n\n//# ifdef JSON_USE_CPPTL\n//   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;\n//   typedef CppTL::AnyEnumerator<const Value &> EnumValues;\n//# endif\n\n/** \\brief Lightweight wrapper to tag static string.\n *\n * Value constructor and objectValue member assignement takes advantage of the\n * StaticString and avoid the cost of string duplication when storing the\n * string or the member name.\n *\n * Example of usage:\n * \\code\n * Json::Value aValue( StaticString(\"some text\") );\n * Json::Value object;\n * static const StaticString code(\"code\");\n * object[code] = 1234;\n * \\endcode\n */\nclass JSON_API StaticString {\n public:\n  explicit StaticString(const char* czstring) : c_str_(czstring) {}\n\n  operator const char*() const { return c_str_; }\n\n  const char* c_str() const { return c_str_; }\n\n private:\n  const char* c_str_;\n};\n\n/** \\brief Represents a <a HREF=\"http://www.json.org\">JSON</a> value.\n *\n * This class is a discriminated union wrapper that can represents a:\n * - signed integer [range: Value::minInt - Value::maxInt]\n * - unsigned integer (range: 0 - Value::maxUInt)\n * - double\n * - UTF-8 string\n * - boolean\n * - 'null'\n * - an ordered list of Value\n * - collection of name/value pairs (javascript object)\n *\n * The type of the held value is represented by a #ValueType and\n * can be obtained using type().\n *\n * Values of an #objectValue or #arrayValue can be accessed using operator[]()\n * methods.\n * Non-const methods will automatically create the a #nullValue element\n * if it does not exist.\n * The sequence of an #arrayValue will be automatically resized and initialized\n * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.\n *\n * The get() methods can be used to obtain default value in the case the\n * required element does not exist.\n *\n * It is possible to iterate over the list of a #objectValue values using\n * the getMemberNames() method.\n *\n * \\note #Value string-length fit in size_t, but keys must be < 2^30.\n * (The reason is an implementation detail.) A #CharReader will raise an\n * exception if a bound is exceeded to avoid security holes in your app,\n * but the Value API does *not* check bounds. That is the responsibility\n * of the caller.\n */\nclass JSON_API Value {\n  friend class ValueIteratorBase;\n\n public:\n  typedef std::vector<JSONCPP_STRING> Members;\n  typedef ValueIterator iterator;\n  typedef ValueConstIterator const_iterator;\n  typedef Json::UInt UInt;\n  typedef Json::Int Int;\n#if defined(JSON_HAS_INT64)\n  typedef Json::UInt64 UInt64;\n  typedef Json::Int64 Int64;\n#endif  // defined(JSON_HAS_INT64)\n  typedef Json::LargestInt LargestInt;\n  typedef Json::LargestUInt LargestUInt;\n  typedef Json::ArrayIndex ArrayIndex;\n\n  static const Value& null;             ///< We regret this reference to a global instance; prefer the simpler Value().\n  static const Value& nullRef;          ///< just a kludge for binary-compatibility; same as null\n  static Value const& nullSingleton();  ///< Prefer this to null or nullRef.\n\n  /// Minimum signed integer value that can be stored in a Json::Value.\n  static const LargestInt minLargestInt;\n  /// Maximum signed integer value that can be stored in a Json::Value.\n  static const LargestInt maxLargestInt;\n  /// Maximum unsigned integer value that can be stored in a Json::Value.\n  static const LargestUInt maxLargestUInt;\n\n  /// Minimum signed int value that can be stored in a Json::Value.\n  static const Int minInt;\n  /// Maximum signed int value that can be stored in a Json::Value.\n  static const Int maxInt;\n  /// Maximum unsigned int value that can be stored in a Json::Value.\n  static const UInt maxUInt;\n\n#if defined(JSON_HAS_INT64)\n  /// Minimum signed 64 bits int value that can be stored in a Json::Value.\n  static const Int64 minInt64;\n  /// Maximum signed 64 bits int value that can be stored in a Json::Value.\n  static const Int64 maxInt64;\n  /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.\n  static const UInt64 maxUInt64;\n#endif  // defined(JSON_HAS_INT64)\n\n private:\n#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION\n  class CZString {\n   public:\n    enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };\n    CZString(ArrayIndex index);\n    CZString(char const* str, unsigned length, DuplicationPolicy allocate);\n    CZString(CZString const& other);\n#if JSON_HAS_RVALUE_REFERENCES\n    CZString(CZString&& other);\n#endif\n    ~CZString();\n    CZString& operator=(CZString other);\n    bool operator<(CZString const& other) const;\n    bool operator==(CZString const& other) const;\n    ArrayIndex index() const;\n    // const char* c_str() const; ///< \\deprecated\n    char const* data() const;\n    unsigned length() const;\n    bool isStaticString() const;\n\n   private:\n    void swap(CZString& other);\n\n    struct StringStorage {\n      unsigned policy_ : 2;\n      unsigned length_ : 30;  // 1GB max\n    };\n\n    char const* cstr_;  // actually, a prefixed string, unless policy is noDup\n    union {\n      ArrayIndex index_;\n      StringStorage storage_;\n    };\n  };\n\n public:\n#ifndef JSON_USE_CPPTL_SMALLMAP\n  typedef std::map<CZString, Value> ObjectValues;\n#else\n  typedef CppTL::SmallMap<CZString, Value> ObjectValues;\n#endif  // ifndef JSON_USE_CPPTL_SMALLMAP\n#endif  // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION\n\n public:\n  /** \\brief Create a default Value of the given type.\n\n    This is a very useful constructor.\n    To create an empty array, pass arrayValue.\n    To create an empty object, pass objectValue.\n    Another Value can then be set to this one by assignment.\nThis is useful since clear() and resize() will not alter types.\n\n    Examples:\n\\code\nJson::Value null_value; // null\nJson::Value arr_value(Json::arrayValue); // []\nJson::Value obj_value(Json::objectValue); // {}\n\\endcode\n  */\n  Value(ValueType type = nullValue);\n  Value(Int value);\n  Value(UInt value);\n#if defined(JSON_HAS_INT64)\n  Value(Int64 value);\n  Value(UInt64 value);\n#endif  // if defined(JSON_HAS_INT64)\n  Value(double value);\n  Value(const char* value);                   ///< Copy til first 0. (NULL causes to seg-fault.)\n  Value(const char* begin, const char* end);  ///< Copy all, incl zeroes.\n  /** \\brief Constructs a value from a static string.\n\n   * Like other value string constructor but do not duplicate the string for\n   * internal storage. The given string must remain alive after the call to this\n   * constructor.\n   * \\note This works only for null-terminated strings. (We cannot change the\n   *   size of this class, so we have nowhere to store the length,\n   *   which might be computed later for various operations.)\n   *\n   * Example of usage:\n   * \\code\n   * static StaticString foo(\"some text\");\n   * Json::Value aValue(foo);\n   * \\endcode\n   */\n  Value(const StaticString& value);\n  Value(const JSONCPP_STRING& value);  ///< Copy data() til size(). Embedded zeroes too.\n#ifdef JSON_USE_CPPTL\n  Value(const CppTL::ConstString& value);\n#endif\n  Value(bool value);\n  /// Deep copy.\n  Value(const Value& other);\n#if JSON_HAS_RVALUE_REFERENCES\n  /// Move constructor\n  Value(Value&& other);\n#endif\n  ~Value();\n\n  /// Deep copy, then swap(other).\n  /// \\note Over-write existing comments. To preserve comments, use #swapPayload().\n  Value& operator=(Value other);\n  /// Swap everything.\n  void swap(Value& other);\n  /// Swap values but leave comments and source offsets in place.\n  void swapPayload(Value& other);\n\n  ValueType type() const;\n\n  /// Compare payload only, not comments etc.\n  bool operator<(const Value& other) const;\n  bool operator<=(const Value& other) const;\n  bool operator>=(const Value& other) const;\n  bool operator>(const Value& other) const;\n  bool operator==(const Value& other) const;\n  bool operator!=(const Value& other) const;\n  int compare(const Value& other) const;\n\n  const char* asCString() const;  ///< Embedded zeroes could cause you trouble!\n#if JSONCPP_USING_SECURE_MEMORY\n  unsigned getCStringLength() const;  // Allows you to understand the length of the CString\n#endif\n  JSONCPP_STRING asString() const;  ///< Embedded zeroes are possible.\n  /** Get raw char* of string-value.\n   *  \\return false if !string. (Seg-fault if str or end are NULL.)\n   */\n  bool getString(char const** begin, char const** end) const;\n#ifdef JSON_USE_CPPTL\n  CppTL::ConstString asConstString() const;\n#endif\n  Int asInt() const;\n  UInt asUInt() const;\n#if defined(JSON_HAS_INT64)\n  Int64 asInt64() const;\n  UInt64 asUInt64() const;\n#endif  // if defined(JSON_HAS_INT64)\n  LargestInt asLargestInt() const;\n  LargestUInt asLargestUInt() const;\n  float asFloat() const;\n  double asDouble() const;\n  bool asBool() const;\n\n  bool isNull() const;\n  bool isBool() const;\n  bool isInt() const;\n  bool isInt64() const;\n  bool isUInt() const;\n  bool isUInt64() const;\n  bool isIntegral() const;\n  bool isDouble() const;\n  bool isNumeric() const;\n  bool isString() const;\n  bool isArray() const;\n  bool isObject() const;\n\n  bool isConvertibleTo(ValueType other) const;\n\n  /// Number of values in array or object\n  ArrayIndex size() const;\n\n  /// \\brief Return true if empty array, empty object, or null;\n  /// otherwise, false.\n  bool empty() const;\n\n  /// Return isNull()\n  bool operator!() const;\n\n  /// Remove all object members and array elements.\n  /// \\pre type() is arrayValue, objectValue, or nullValue\n  /// \\post type() is unchanged\n  void clear();\n\n  /// Resize the array to size elements.\n  /// New elements are initialized to null.\n  /// May only be called on nullValue or arrayValue.\n  /// \\pre type() is arrayValue or nullValue\n  /// \\post type() is arrayValue\n  void resize(ArrayIndex size);\n\n  /// Access an array element (zero based index ).\n  /// If the array contains less than index element, then null value are\n  /// inserted\n  /// in the array so that its size is index+1.\n  /// (You may need to say 'value[0u]' to get your compiler to distinguish\n  ///  this from the operator[] which takes a string.)\n  Value& operator[](ArrayIndex index);\n\n  /// Access an array element (zero based index ).\n  /// If the array contains less than index element, then null value are\n  /// inserted\n  /// in the array so that its size is index+1.\n  /// (You may need to say 'value[0u]' to get your compiler to distinguish\n  ///  this from the operator[] which takes a string.)\n  Value& operator[](int index);\n\n  /// Access an array element (zero based index )\n  /// (You may need to say 'value[0u]' to get your compiler to distinguish\n  ///  this from the operator[] which takes a string.)\n  const Value& operator[](ArrayIndex index) const;\n\n  /// Access an array element (zero based index )\n  /// (You may need to say 'value[0u]' to get your compiler to distinguish\n  ///  this from the operator[] which takes a string.)\n  const Value& operator[](int index) const;\n\n  /// If the array contains at least index+1 elements, returns the element\n  /// value,\n  /// otherwise returns defaultValue.\n  Value get(ArrayIndex index, const Value& defaultValue) const;\n  /// Return true if index < size().\n  bool isValidIndex(ArrayIndex index) const;\n  /// \\brief Append value to array at the end.\n  ///\n  /// Equivalent to jsonvalue[jsonvalue.size()] = value;\n  Value& append(const Value& value);\n\n  /// Access an object value by name, create a null member if it does not exist.\n  /// \\note Because of our implementation, keys are limited to 2^30 -1 chars.\n  ///  Exceeding that will cause an exception.\n  Value& operator[](const char* key);\n  /// Access an object value by name, returns null if there is no member with\n  /// that name.\n  const Value& operator[](const char* key) const;\n  /// Access an object value by name, create a null member if it does not exist.\n  /// \\param key may contain embedded nulls.\n  Value& operator[](const JSONCPP_STRING& key);\n  /// Access an object value by name, returns null if there is no member with\n  /// that name.\n  /// \\param key may contain embedded nulls.\n  const Value& operator[](const JSONCPP_STRING& key) const;\n  /** \\brief Access an object value by name, create a null member if it does not\n   exist.\n\n   * If the object has no entry for that name, then the member name used to store\n   * the new entry is not duplicated.\n   * Example of use:\n   * \\code\n   * Json::Value object;\n   * static const StaticString code(\"code\");\n   * object[code] = 1234;\n   * \\endcode\n   */\n  Value& operator[](const StaticString& key);\n#ifdef JSON_USE_CPPTL\n  /// Access an object value by name, create a null member if it does not exist.\n  Value& operator[](const CppTL::ConstString& key);\n  /// Access an object value by name, returns null if there is no member with\n  /// that name.\n  const Value& operator[](const CppTL::ConstString& key) const;\n#endif\n  /// Return the member named key if it exist, defaultValue otherwise.\n  /// \\note deep copy\n  Value get(const char* key, const Value& defaultValue) const;\n  /// Return the member named key if it exist, defaultValue otherwise.\n  /// \\note deep copy\n  /// \\note key may contain embedded nulls.\n  Value get(const char* begin, const char* end, const Value& defaultValue) const;\n  /// Return the member named key if it exist, defaultValue otherwise.\n  /// \\note deep copy\n  /// \\param key may contain embedded nulls.\n  Value get(const JSONCPP_STRING& key, const Value& defaultValue) const;\n#ifdef JSON_USE_CPPTL\n  /// Return the member named key if it exist, defaultValue otherwise.\n  /// \\note deep copy\n  Value get(const CppTL::ConstString& key, const Value& defaultValue) const;\n#endif\n  /// Most general and efficient version of isMember()const, get()const,\n  /// and operator[]const\n  /// \\note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30\n  Value const* find(char const* begin, char const* end) const;\n  /// Most general and efficient version of object-mutators.\n  /// \\note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30\n  /// \\return non-zero, but JSON_ASSERT if this is neither object nor nullValue.\n  Value const* demand(char const* begin, char const* end);\n  /// \\brief Remove and return the named member.\n  ///\n  /// Do nothing if it did not exist.\n  /// \\return the removed Value, or null.\n  /// \\pre type() is objectValue or nullValue\n  /// \\post type() is unchanged\n  /// \\deprecated\n  Value removeMember(const char* key);\n  /// Same as removeMember(const char*)\n  /// \\param key may contain embedded nulls.\n  /// \\deprecated\n  Value removeMember(const JSONCPP_STRING& key);\n  /// Same as removeMember(const char* begin, const char* end, Value* removed),\n  /// but 'key' is null-terminated.\n  bool removeMember(const char* key, Value* removed);\n  /** \\brief Remove the named map member.\n\n      Update 'removed' iff removed.\n      \\param key may contain embedded nulls.\n      \\return true iff removed (no exceptions)\n  */\n  bool removeMember(JSONCPP_STRING const& key, Value* removed);\n  /// Same as removeMember(JSONCPP_STRING const& key, Value* removed)\n  bool removeMember(const char* begin, const char* end, Value* removed);\n  /** \\brief Remove the indexed array element.\n\n      O(n) expensive operations.\n      Update 'removed' iff removed.\n      \\return true iff removed (no exceptions)\n  */\n  bool removeIndex(ArrayIndex i, Value* removed);\n\n  /// Return true if the object has a member named key.\n  /// \\note 'key' must be null-terminated.\n  bool isMember(const char* key) const;\n  /// Return true if the object has a member named key.\n  /// \\param key may contain embedded nulls.\n  bool isMember(const JSONCPP_STRING& key) const;\n  /// Same as isMember(JSONCPP_STRING const& key)const\n  bool isMember(const char* begin, const char* end) const;\n#ifdef JSON_USE_CPPTL\n  /// Return true if the object has a member named key.\n  bool isMember(const CppTL::ConstString& key) const;\n#endif\n\n  /// \\brief Return a list of the member names.\n  ///\n  /// If null, return an empty list.\n  /// \\pre type() is objectValue or nullValue\n  /// \\post if type() was nullValue, it remains nullValue\n  Members getMemberNames() const;\n\n  //# ifdef JSON_USE_CPPTL\n  //      EnumMemberNames enumMemberNames() const;\n  //      EnumValues enumValues() const;\n  //# endif\n\n  /// \\deprecated Always pass len.\n  JSONCPP_DEPRECATED(\"Use setComment(JSONCPP_STRING const&) instead.\")\n  void setComment(const char* comment, CommentPlacement placement);\n  /// Comments must be //... or /* ... */\n  void setComment(const char* comment, size_t len, CommentPlacement placement);\n  /// Comments must be //... or /* ... */\n  void setComment(const JSONCPP_STRING& comment, CommentPlacement placement);\n  bool hasComment(CommentPlacement placement) const;\n  /// Include delimiters and embedded newlines.\n  JSONCPP_STRING getComment(CommentPlacement placement) const;\n\n  JSONCPP_STRING toStyledString() const;\n\n  const_iterator begin() const;\n  const_iterator end() const;\n\n  iterator begin();\n  iterator end();\n\n  // Accessors for the [start, limit) range of bytes within the JSON text from\n  // which this value was parsed, if any.\n  void setOffsetStart(ptrdiff_t start);\n  void setOffsetLimit(ptrdiff_t limit);\n  ptrdiff_t getOffsetStart() const;\n  ptrdiff_t getOffsetLimit() const;\n\n private:\n  void initBasic(ValueType type, bool allocated = false);\n\n  Value& resolveReference(const char* key);\n  Value& resolveReference(const char* key, const char* end);\n\n  struct CommentInfo {\n    CommentInfo();\n    ~CommentInfo();\n\n    void setComment(const char* text, size_t len);\n\n    char* comment_;\n  };\n\n  // struct MemberNamesTransform\n  //{\n  //   typedef const char *result_type;\n  //   const char *operator()( const CZString &name ) const\n  //   {\n  //      return name.c_str();\n  //   }\n  //};\n\n  union ValueHolder {\n    LargestInt int_;\n    LargestUInt uint_;\n    double real_;\n    bool bool_;\n    char* string_;  // actually ptr to unsigned, followed by str, unless !allocated_\n    ObjectValues* map_;\n  } value_;\n  ValueType type_ : 8;\n  unsigned int allocated_ : 1;  // Notes: if declared as bool, bitfield is useless.\n                                // If not allocated_, string_ must be null-terminated.\n  CommentInfo* comments_;\n\n  // [start, limit) byte offsets in the source JSON text from which this Value\n  // was extracted.\n  ptrdiff_t start_;\n  ptrdiff_t limit_;\n};\n\n/** \\brief Experimental and untested: represents an element of the \"path\" to\n * access a node.\n */\nclass JSON_API PathArgument {\n public:\n  friend class Path;\n\n  PathArgument();\n  PathArgument(ArrayIndex index);\n  PathArgument(const char* key);\n  PathArgument(const JSONCPP_STRING& key);\n\n private:\n  enum Kind { kindNone = 0, kindIndex, kindKey };\n  JSONCPP_STRING key_;\n  ArrayIndex index_;\n  Kind kind_;\n};\n\n/** \\brief Experimental and untested: represents a \"path\" to access a node.\n *\n * Syntax:\n * - \".\" => root node\n * - \".[n]\" => elements at index 'n' of root node (an array value)\n * - \".name\" => member named 'name' of root node (an object value)\n * - \".name1.name2.name3\"\n * - \".[0][1][2].name1[3]\"\n * - \".%\" => member name is provided as parameter\n * - \".[%]\" => index is provied as parameter\n */\nclass JSON_API Path {\n public:\n  Path(const JSONCPP_STRING& path, const PathArgument& a1 = PathArgument(), const PathArgument& a2 = PathArgument(),\n       const PathArgument& a3 = PathArgument(), const PathArgument& a4 = PathArgument(), const PathArgument& a5 = PathArgument());\n\n  const Value& resolve(const Value& root) const;\n  Value resolve(const Value& root, const Value& defaultValue) const;\n  /// Creates the \"path\" to access the specified node and returns a reference on\n  /// the node.\n  Value& make(Value& root) const;\n\n private:\n  typedef std::vector<const PathArgument*> InArgs;\n  typedef std::vector<PathArgument> Args;\n\n  void makePath(const JSONCPP_STRING& path, const InArgs& in);\n  void addPathInArg(const JSONCPP_STRING& path, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind);\n  void invalidPath(const JSONCPP_STRING& path, int location);\n\n  Args args_;\n};\n\n/** \\brief base class for Value iterators.\n *\n */\nclass JSON_API ValueIteratorBase {\n public:\n  typedef std::bidirectional_iterator_tag iterator_category;\n  typedef unsigned int size_t;\n  typedef int difference_type;\n  typedef ValueIteratorBase SelfType;\n\n  bool operator==(const SelfType& other) const { return isEqual(other); }\n\n  bool operator!=(const SelfType& other) const { return !isEqual(other); }\n\n  difference_type operator-(const SelfType& other) const { return other.computeDistance(*this); }\n\n  /// Return either the index or the member name of the referenced value as a\n  /// Value.\n  Value key() const;\n\n  /// Return the index of the referenced Value, or -1 if it is not an arrayValue.\n  UInt index() const;\n\n  /// Return the member name of the referenced Value, or \"\" if it is not an\n  /// objectValue.\n  /// \\note Avoid `c_str()` on result, as embedded zeroes are possible.\n  JSONCPP_STRING name() const;\n\n  /// Return the member name of the referenced Value. \"\" if it is not an\n  /// objectValue.\n  /// \\deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls.\n  JSONCPP_DEPRECATED(\"Use `key = name();` instead.\")\n  char const* memberName() const;\n  /// Return the member name of the referenced Value, or NULL if it is not an\n  /// objectValue.\n  /// \\note Better version than memberName(). Allows embedded nulls.\n  char const* memberName(char const** end) const;\n\n protected:\n  Value& deref() const;\n\n  void increment();\n\n  void decrement();\n\n  difference_type computeDistance(const SelfType& other) const;\n\n  bool isEqual(const SelfType& other) const;\n\n  void copy(const SelfType& other);\n\n private:\n  Value::ObjectValues::iterator current_;\n  // Indicates that iterator is for a null value.\n  bool isNull_;\n\n public:\n  // For some reason, BORLAND needs these at the end, rather\n  // than earlier. No idea why.\n  ValueIteratorBase();\n  explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);\n};\n\n/** \\brief const iterator for object and array value.\n *\n */\nclass JSON_API ValueConstIterator : public ValueIteratorBase {\n  friend class Value;\n\n public:\n  typedef const Value value_type;\n  // typedef unsigned int size_t;\n  // typedef int difference_type;\n  typedef const Value& reference;\n  typedef const Value* pointer;\n  typedef ValueConstIterator SelfType;\n\n  ValueConstIterator();\n  ValueConstIterator(ValueIterator const& other);\n\n private:\n  /*! \\internal Use by Value to create an iterator.\n   */\n  explicit ValueConstIterator(const Value::ObjectValues::iterator& current);\n\n public:\n  SelfType& operator=(const ValueIteratorBase& other);\n\n  SelfType operator++(int) {\n    SelfType temp(*this);\n    ++*this;\n    return temp;\n  }\n\n  SelfType operator--(int) {\n    SelfType temp(*this);\n    --*this;\n    return temp;\n  }\n\n  SelfType& operator--() {\n    decrement();\n    return *this;\n  }\n\n  SelfType& operator++() {\n    increment();\n    return *this;\n  }\n\n  reference operator*() const { return deref(); }\n\n  pointer operator->() const { return &deref(); }\n};\n\n/** \\brief Iterator for object and array value.\n */\nclass JSON_API ValueIterator : public ValueIteratorBase {\n  friend class Value;\n\n public:\n  typedef Value value_type;\n  typedef unsigned int size_t;\n  typedef int difference_type;\n  typedef Value& reference;\n  typedef Value* pointer;\n  typedef ValueIterator SelfType;\n\n  ValueIterator();\n  explicit ValueIterator(const ValueConstIterator& other);\n  ValueIterator(const ValueIterator& other);\n\n private:\n  /*! \\internal Use by Value to create an iterator.\n   */\n  explicit ValueIterator(const Value::ObjectValues::iterator& current);\n\n public:\n  SelfType& operator=(const SelfType& other);\n\n  SelfType operator++(int) {\n    SelfType temp(*this);\n    ++*this;\n    return temp;\n  }\n\n  SelfType operator--(int) {\n    SelfType temp(*this);\n    --*this;\n    return temp;\n  }\n\n  SelfType& operator--() {\n    decrement();\n    return *this;\n  }\n\n  SelfType& operator++() {\n    increment();\n    return *this;\n  }\n\n  reference operator*() const { return deref(); }\n\n  pointer operator->() const { return &deref(); }\n};\n\n}  // namespace Json\n\nnamespace std {\n/// Specialize std::swap() for Json::Value.\ntemplate <>\ninline void swap(Json::Value& a, Json::Value& b) {\n  a.swap(b);\n}\n}\n\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(pop)\n#endif  // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\n#endif  // CPPTL_JSON_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/value.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/reader.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef CPPTL_JSON_READER_H_INCLUDED\n#define CPPTL_JSON_READER_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"features.h\"\n#include \"value.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n#include <deque>\n#include <iosfwd>\n#include <istream>\n#include <stack>\n#include <string>\n\n// Disable warning C4251: <data member>: <type> needs to have dll-interface to\n// be used by...\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#endif  // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\nnamespace Json {\n\n/** \\brief Unserialize a <a HREF=\"http://www.json.org\">JSON</a> document into a\n *Value.\n *\n * \\deprecated Use CharReader and CharReaderBuilder.\n */\nclass JSON_API Reader {\n public:\n  typedef char Char;\n  typedef const Char* Location;\n\n  /** \\brief An error tagged with where in the JSON text it was encountered.\n   *\n   * The offsets give the [start, limit) range of bytes within the text. Note\n   * that this is bytes, not codepoints.\n   *\n   */\n  struct StructuredError {\n    ptrdiff_t offset_start;\n    ptrdiff_t offset_limit;\n    JSONCPP_STRING message;\n  };\n\n  /** \\brief Constructs a Reader allowing all features\n   * for parsing.\n   */\n  Reader();\n\n  /** \\brief Constructs a Reader allowing the specified feature set\n   * for parsing.\n   */\n  Reader(const Features& features);\n\n  /** \\brief Read a Value from a <a HREF=\"http://www.json.org\">JSON</a>\n   * document.\n   * \\param document UTF-8 encoded string containing the document to read.\n   * \\param root [out] Contains the root value of the document if it was\n   *             successfully parsed.\n   * \\param collectComments \\c true to collect comment and allow writing them\n   * back during\n   *                        serialization, \\c false to discard comments.\n   *                        This parameter is ignored if\n   * Features::allowComments_\n   *                        is \\c false.\n   * \\return \\c true if the document was successfully parsed, \\c false if an\n   * error occurred.\n   */\n  bool parse(const std::string& document, Value& root, bool collectComments = true);\n\n  /** \\brief Read a Value from a <a HREF=\"http://www.json.org\">JSON</a>\n   document.\n   * \\param beginDoc Pointer on the beginning of the UTF-8 encoded string of the\n   document to read.\n   * \\param endDoc Pointer on the end of the UTF-8 encoded string of the\n   document to read.\n   *               Must be >= beginDoc.\n   * \\param root [out] Contains the root value of the document if it was\n   *             successfully parsed.\n   * \\param collectComments \\c true to collect comment and allow writing them\n   back during\n   *                        serialization, \\c false to discard comments.\n   *                        This parameter is ignored if\n   Features::allowComments_\n   *                        is \\c false.\n   * \\return \\c true if the document was successfully parsed, \\c false if an\n   error occurred.\n   */\n  bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true);\n\n  /// \\brief Parse from input stream.\n  /// \\see Json::operator>>(std::istream&, Json::Value&).\n  bool parse(JSONCPP_ISTREAM& is, Value& root, bool collectComments = true);\n\n  /** \\brief Returns a user friendly string that list errors in the parsed\n   * document.\n   * \\return Formatted error message with the list of errors with their location\n   * in\n   *         the parsed document. An empty string is returned if no error\n   * occurred\n   *         during parsing.\n   * \\deprecated Use getFormattedErrorMessages() instead (typo fix).\n   */\n  JSONCPP_DEPRECATED(\"Use getFormattedErrorMessages() instead.\")\n  JSONCPP_STRING getFormatedErrorMessages() const;\n\n  /** \\brief Returns a user friendly string that list errors in the parsed\n   * document.\n   * \\return Formatted error message with the list of errors with their location\n   * in\n   *         the parsed document. An empty string is returned if no error\n   * occurred\n   *         during parsing.\n   */\n  JSONCPP_STRING getFormattedErrorMessages() const;\n\n  /** \\brief Returns a vector of structured erros encounted while parsing.\n   * \\return A (possibly empty) vector of StructuredError objects. Currently\n   *         only one error can be returned, but the caller should tolerate\n   * multiple\n   *         errors.  This can occur if the parser recovers from a non-fatal\n   *         parse error and then encounters additional errors.\n   */\n  std::vector<StructuredError> getStructuredErrors() const;\n\n  /** \\brief Add a semantic error message.\n   * \\param value JSON Value location associated with the error\n   * \\param message The error message.\n   * \\return \\c true if the error was successfully added, \\c false if the\n   * Value offset exceeds the document size.\n   */\n  bool pushError(const Value& value, const JSONCPP_STRING& message);\n\n  /** \\brief Add a semantic error message with extra context.\n   * \\param value JSON Value location associated with the error\n   * \\param message The error message.\n   * \\param extra Additional JSON Value location to contextualize the error\n   * \\return \\c true if the error was successfully added, \\c false if either\n   * Value offset exceeds the document size.\n   */\n  bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra);\n\n  /** \\brief Return whether there are any errors.\n   * \\return \\c true if there are no errors to report \\c false if\n   * errors have occurred.\n   */\n  bool good() const;\n\n private:\n  enum TokenType {\n    tokenEndOfStream = 0,\n    tokenObjectBegin,\n    tokenObjectEnd,\n    tokenArrayBegin,\n    tokenArrayEnd,\n    tokenString,\n    tokenNumber,\n    tokenTrue,\n    tokenFalse,\n    tokenNull,\n    tokenArraySeparator,\n    tokenMemberSeparator,\n    tokenComment,\n    tokenError\n  };\n\n  class Token {\n   public:\n    TokenType type_;\n    Location start_;\n    Location end_;\n  };\n\n  class ErrorInfo {\n   public:\n    Token token_;\n    JSONCPP_STRING message_;\n    Location extra_;\n  };\n\n  typedef std::deque<ErrorInfo> Errors;\n\n  bool readToken(Token& token);\n  void skipSpaces();\n  bool match(Location pattern, int patternLength);\n  bool readComment();\n  bool readCStyleComment();\n  bool readCppStyleComment();\n  bool readString();\n  void readNumber();\n  bool readValue();\n  bool readObject(Token& token);\n  bool readArray(Token& token);\n  bool decodeNumber(Token& token);\n  bool decodeNumber(Token& token, Value& decoded);\n  bool decodeString(Token& token);\n  bool decodeString(Token& token, JSONCPP_STRING& decoded);\n  bool decodeDouble(Token& token);\n  bool decodeDouble(Token& token, Value& decoded);\n  bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode);\n  bool decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode);\n  bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0);\n  bool recoverFromError(TokenType skipUntilToken);\n  bool addErrorAndRecover(const JSONCPP_STRING& message, Token& token, TokenType skipUntilToken);\n  void skipUntilSpace();\n  Value& currentValue();\n  Char getNextChar();\n  void getLocationLineAndColumn(Location location, int& line, int& column) const;\n  JSONCPP_STRING getLocationLineAndColumn(Location location) const;\n  void addComment(Location begin, Location end, CommentPlacement placement);\n  void skipCommentTokens(Token& token);\n\n  typedef std::stack<Value*> Nodes;\n  Nodes nodes_;\n  Errors errors_;\n  JSONCPP_STRING document_;\n  Location begin_;\n  Location end_;\n  Location current_;\n  Location lastValueEnd_;\n  Value* lastValue_;\n  JSONCPP_STRING commentsBefore_;\n  Features features_;\n  bool collectComments_;\n};  // Reader\n\n/** Interface for reading JSON from a char array.\n */\nclass JSON_API CharReader {\n public:\n  virtual ~CharReader() {}\n  /** \\brief Read a Value from a <a HREF=\"http://www.json.org\">JSON</a>\n   document.\n   * The document must be a UTF-8 encoded string containing the document to read.\n   *\n   * \\param beginDoc Pointer on the beginning of the UTF-8 encoded string of the\n   document to read.\n   * \\param endDoc Pointer on the end of the UTF-8 encoded string of the\n   document to read.\n   *        Must be >= beginDoc.\n   * \\param root [out] Contains the root value of the document if it was\n   *             successfully parsed.\n   * \\param errs [out] Formatted error messages (if not NULL)\n   *        a user friendly string that lists errors in the parsed\n   * document.\n   * \\return \\c true if the document was successfully parsed, \\c false if an\n   error occurred.\n   */\n  virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, JSONCPP_STRING* errs) = 0;\n\n  class JSON_API Factory {\n   public:\n    virtual ~Factory() {}\n    /** \\brief Allocate a CharReader via operator new().\n     * \\throw std::exception if something goes wrong (e.g. invalid settings)\n     */\n    virtual CharReader* newCharReader() const = 0;\n  };  // Factory\n};    // CharReader\n\n/** \\brief Build a CharReader implementation.\n\nUsage:\n\\code\n  using namespace Json;\n  CharReaderBuilder builder;\n  builder[\"collectComments\"] = false;\n  Value value;\n  JSONCPP_STRING errs;\n  bool ok = parseFromStream(builder, std::cin, &value, &errs);\n\\endcode\n*/\nclass JSON_API CharReaderBuilder : public CharReader::Factory {\n public:\n  // Note: We use a Json::Value so that we can add data-members to this class\n  // without a major version bump.\n  /** Configuration of this builder.\n    These are case-sensitive.\n    Available settings (case-sensitive):\n    - `\"collectComments\": false or true`\n      - true to collect comment and allow writing them\n        back during serialization, false to discard comments.\n        This parameter is ignored if allowComments is false.\n    - `\"allowComments\": false or true`\n      - true if comments are allowed.\n    - `\"strictRoot\": false or true`\n      - true if root must be either an array or an object value\n    - `\"allowDroppedNullPlaceholders\": false or true`\n      - true if dropped null placeholders are allowed. (See StreamWriterBuilder.)\n    - `\"allowNumericKeys\": false or true`\n      - true if numeric object keys are allowed.\n    - `\"allowSingleQuotes\": false or true`\n      - true if '' are allowed for strings (both keys and values)\n    - `\"stackLimit\": integer`\n      - Exceeding stackLimit (recursive depth of `readValue()`) will\n        cause an exception.\n      - This is a security issue (seg-faults caused by deeply nested JSON),\n        so the default is low.\n    - `\"failIfExtra\": false or true`\n      - If true, `parse()` returns false when extra non-whitespace trails\n        the JSON value in the input string.\n    - `\"rejectDupKeys\": false or true`\n      - If true, `parse()` returns false when a key is duplicated within an object.\n    - `\"allowSpecialFloats\": false or true`\n      - If true, special float values (NaNs and infinities) are allowed\n        and their values are lossfree restorable.\n\n    You can examine 'settings_` yourself\n    to see the defaults. You can also write and read them just like any\n    JSON Value.\n    \\sa setDefaults()\n    */\n  Json::Value settings_;\n\n  CharReaderBuilder();\n  ~CharReaderBuilder() JSONCPP_OVERRIDE;\n\n  CharReader* newCharReader() const JSONCPP_OVERRIDE;\n\n  /** \\return true if 'settings' are legal and consistent;\n   *   otherwise, indicate bad settings via 'invalid'.\n   */\n  bool validate(Json::Value* invalid) const;\n\n  /** A simple way to update a specific setting.\n   */\n  Value& operator[](JSONCPP_STRING key);\n\n  /** Called by ctor, but you can use this to reset settings_.\n   * \\pre 'settings' != NULL (but Json::null is fine)\n   * \\remark Defaults:\n   * \\snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults\n   */\n  static void setDefaults(Json::Value* settings);\n  /** Same as old Features::strictMode().\n   * \\pre 'settings' != NULL (but Json::null is fine)\n   * \\remark Defaults:\n   * \\snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode\n   */\n  static void strictMode(Json::Value* settings);\n};\n\n/** Consume entire stream and use its begin/end.\n  * Someday we might have a real StreamReader, but for now this\n  * is convenient.\n  */\nbool JSON_API parseFromStream(CharReader::Factory const&, JSONCPP_ISTREAM&, Value* root, std::string* errs);\n\n/** \\brief Read from 'sin' into 'root'.\n\n Always keep comments from the input JSON.\n\n This can be used to read a file into a particular sub-object.\n For example:\n \\code\n Json::Value root;\n cin >> root[\"dir\"][\"file\"];\n cout << root;\n \\endcode\n Result:\n \\verbatim\n {\n \"dir\": {\n     \"file\": {\n     // The input stream JSON would be nested here.\n     }\n }\n }\n \\endverbatim\n \\throw std::exception on parse error.\n \\see Json::operator<<()\n*/\nJSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&);\n\n}  // namespace Json\n\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(pop)\n#endif  // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\n#endif  // CPPTL_JSON_READER_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/reader.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/writer.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef JSON_WRITER_H_INCLUDED\n#define JSON_WRITER_H_INCLUDED\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"value.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n#include <ostream>\n#include <string>\n#include <vector>\n\n// Disable warning C4251: <data member>: <type> needs to have dll-interface to\n// be used by...\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(push)\n#pragma warning(disable : 4251)\n#endif  // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\nnamespace Json {\n\nclass Value;\n\n/**\n\nUsage:\n\\code\n  using namespace Json;\n  void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {\n    std::unique_ptr<StreamWriter> const writer(\n      factory.newStreamWriter());\n    writer->write(value, &std::cout);\n    std::cout << std::endl;  // add lf and flush\n  }\n\\endcode\n*/\nclass JSON_API StreamWriter {\n protected:\n  JSONCPP_OSTREAM* sout_;  // not owned; will not delete\n public:\n  StreamWriter();\n  virtual ~StreamWriter();\n  /** Write Value into document as configured in sub-class.\n      Do not take ownership of sout, but maintain a reference during function.\n      \\pre sout != NULL\n      \\return zero on success (For now, we always return zero, so check the stream instead.)\n      \\throw std::exception possibly, depending on configuration\n   */\n  virtual int write(Value const& root, JSONCPP_OSTREAM* sout) = 0;\n\n  /** \\brief A simple abstract factory.\n   */\n  class JSON_API Factory {\n   public:\n    virtual ~Factory();\n    /** \\brief Allocate a CharReader via operator new().\n     * \\throw std::exception if something goes wrong (e.g. invalid settings)\n     */\n    virtual StreamWriter* newStreamWriter() const = 0;\n  };  // Factory\n};    // StreamWriter\n\n/** \\brief Write into stringstream, then return string, for convenience.\n * A StreamWriter will be created from the factory, used, and then deleted.\n */\nJSONCPP_STRING JSON_API writeString(StreamWriter::Factory const& factory, Value const& root);\n\n/** \\brief Build a StreamWriter implementation.\n\nUsage:\n\\code\n  using namespace Json;\n  Value value = ...;\n  StreamWriterBuilder builder;\n  builder[\"commentStyle\"] = \"None\";\n  builder[\"indentation\"] = \"   \";  // or whatever you like\n  std::unique_ptr<Json::StreamWriter> writer(\n      builder.newStreamWriter());\n  writer->write(value, &std::cout);\n  std::cout << std::endl;  // add lf and flush\n\\endcode\n*/\nclass JSON_API StreamWriterBuilder : public StreamWriter::Factory {\n public:\n  // Note: We use a Json::Value so that we can add data-members to this class\n  // without a major version bump.\n  /** Configuration of this builder.\n    Available settings (case-sensitive):\n    - \"commentStyle\": \"None\" or \"All\"\n    - \"indentation\":  \"<anything>\"\n    - \"enableYAMLCompatibility\": false or true\n      - slightly change the whitespace around colons\n    - \"dropNullPlaceholders\": false or true\n      - Drop the \"null\" string from the writer's output for nullValues.\n        Strictly speaking, this is not valid JSON. But when the output is being\n        fed to a browser's Javascript, it makes for smaller output and the\n        browser can handle the output just fine.\n    - \"useSpecialFloats\": false or true\n      - If true, outputs non-finite floating point values in the following way:\n        NaN values as \"NaN\", positive infinity as \"Infinity\", and negative infinity\n        as \"-Infinity\".\n\n    You can examine 'settings_` yourself\n    to see the defaults. You can also write and read them just like any\n    JSON Value.\n    \\sa setDefaults()\n    */\n  Json::Value settings_;\n\n  StreamWriterBuilder();\n  ~StreamWriterBuilder() JSONCPP_OVERRIDE;\n\n  /**\n   * \\throw std::exception if something goes wrong (e.g. invalid settings)\n   */\n  StreamWriter* newStreamWriter() const JSONCPP_OVERRIDE;\n\n  /** \\return true if 'settings' are legal and consistent;\n   *   otherwise, indicate bad settings via 'invalid'.\n   */\n  bool validate(Json::Value* invalid) const;\n  /** A simple way to update a specific setting.\n   */\n  Value& operator[](JSONCPP_STRING key);\n\n  /** Called by ctor, but you can use this to reset settings_.\n   * \\pre 'settings' != NULL (but Json::null is fine)\n   * \\remark Defaults:\n   * \\snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults\n   */\n  static void setDefaults(Json::Value* settings);\n};\n\n/** \\brief Abstract class for writers.\n * \\deprecated Use StreamWriter. (And really, this is an implementation detail.)\n */\nclass JSON_API Writer {\n public:\n  virtual ~Writer();\n\n  virtual JSONCPP_STRING write(const Value& root) = 0;\n};\n\n/** \\brief Outputs a Value in <a HREF=\"http://www.json.org\">JSON</a> format\n *without formatting (not human friendly).\n *\n * The JSON document is written in a single line. It is not intended for 'human'\n *consumption,\n * but may be usefull to support feature such as RPC where bandwith is limited.\n * \\sa Reader, Value\n * \\deprecated Use StreamWriterBuilder.\n */\nclass JSON_API FastWriter : public Writer {\n public:\n  FastWriter();\n  ~FastWriter() JSONCPP_OVERRIDE {}\n\n  void enableYAMLCompatibility();\n\n  /** \\brief Drop the \"null\" string from the writer's output for nullValues.\n   * Strictly speaking, this is not valid JSON. But when the output is being\n   * fed to a browser's Javascript, it makes for smaller output and the\n   * browser can handle the output just fine.\n   */\n  void dropNullPlaceholders();\n\n  void omitEndingLineFeed();\n\n public:  // overridden from Writer\n  JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE;\n\n private:\n  void writeValue(const Value& value);\n\n  JSONCPP_STRING document_;\n  bool yamlCompatiblityEnabled_;\n  bool dropNullPlaceholders_;\n  bool omitEndingLineFeed_;\n};\n\n/** \\brief Writes a Value in <a HREF=\"http://www.json.org\">JSON</a> format in a\n *human friendly way.\n *\n * The rules for line break and indent are as follow:\n * - Object value:\n *     - if empty then print {} without indent and line break\n *     - if not empty the print '{', line break & indent, print one value per\n *line\n *       and then unindent and line break and print '}'.\n * - Array value:\n *     - if empty then print [] without indent and line break\n *     - if the array contains no object value, empty array or some other value\n *types,\n *       and all the values fit on one lines, then print the array on a single\n *line.\n *     - otherwise, it the values do not fit on one line, or the array contains\n *       object or non empty array, then print one value per line.\n *\n * If the Value have comments then they are outputed according to their\n *#CommentPlacement.\n *\n * \\sa Reader, Value, Value::setComment()\n * \\deprecated Use StreamWriterBuilder.\n */\nclass JSON_API StyledWriter : public Writer {\n public:\n  StyledWriter();\n  ~StyledWriter() JSONCPP_OVERRIDE {}\n\n public:  // overridden from Writer\n  /** \\brief Serialize a Value in <a HREF=\"http://www.json.org\">JSON</a> format.\n   * \\param root Value to serialize.\n   * \\return String containing the JSON document that represents the root value.\n   */\n  JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE;\n\n private:\n  void writeValue(const Value& value);\n  void writeArrayValue(const Value& value);\n  bool isMultineArray(const Value& value);\n  void pushValue(const JSONCPP_STRING& value);\n  void writeIndent();\n  void writeWithIndent(const JSONCPP_STRING& value);\n  void indent();\n  void unindent();\n  void writeCommentBeforeValue(const Value& root);\n  void writeCommentAfterValueOnSameLine(const Value& root);\n  bool hasCommentForValue(const Value& value);\n  static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text);\n\n  typedef std::vector<JSONCPP_STRING> ChildValues;\n\n  ChildValues childValues_;\n  JSONCPP_STRING document_;\n  JSONCPP_STRING indentString_;\n  unsigned int rightMargin_;\n  unsigned int indentSize_;\n  bool addChildValues_;\n};\n\n/** \\brief Writes a Value in <a HREF=\"http://www.json.org\">JSON</a> format in a\n human friendly way,\n     to a stream rather than to a string.\n *\n * The rules for line break and indent are as follow:\n * - Object value:\n *     - if empty then print {} without indent and line break\n *     - if not empty the print '{', line break & indent, print one value per\n line\n *       and then unindent and line break and print '}'.\n * - Array value:\n *     - if empty then print [] without indent and line break\n *     - if the array contains no object value, empty array or some other value\n types,\n *       and all the values fit on one lines, then print the array on a single\n line.\n *     - otherwise, it the values do not fit on one line, or the array contains\n *       object or non empty array, then print one value per line.\n *\n * If the Value have comments then they are outputed according to their\n #CommentPlacement.\n *\n * \\param indentation Each level will be indented by this amount extra.\n * \\sa Reader, Value, Value::setComment()\n * \\deprecated Use StreamWriterBuilder.\n */\nclass JSON_API StyledStreamWriter {\n public:\n  StyledStreamWriter(JSONCPP_STRING indentation = \"\\t\");\n  ~StyledStreamWriter() {}\n\n public:\n  /** \\brief Serialize a Value in <a HREF=\"http://www.json.org\">JSON</a> format.\n   * \\param out Stream to write to. (Can be ostringstream, e.g.)\n   * \\param root Value to serialize.\n   * \\note There is no point in deriving from Writer, since write() should not\n   * return a value.\n   */\n  void write(JSONCPP_OSTREAM& out, const Value& root);\n\n private:\n  void writeValue(const Value& value);\n  void writeArrayValue(const Value& value);\n  bool isMultineArray(const Value& value);\n  void pushValue(const JSONCPP_STRING& value);\n  void writeIndent();\n  void writeWithIndent(const JSONCPP_STRING& value);\n  void indent();\n  void unindent();\n  void writeCommentBeforeValue(const Value& root);\n  void writeCommentAfterValueOnSameLine(const Value& root);\n  bool hasCommentForValue(const Value& value);\n  static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text);\n\n  typedef std::vector<JSONCPP_STRING> ChildValues;\n\n  ChildValues childValues_;\n  JSONCPP_OSTREAM* document_;\n  JSONCPP_STRING indentString_;\n  unsigned int rightMargin_;\n  JSONCPP_STRING indentation_;\n  bool addChildValues_ : 1;\n  bool indented_ : 1;\n};\n\n#if defined(JSON_HAS_INT64)\nJSONCPP_STRING JSON_API valueToString(Int value);\nJSONCPP_STRING JSON_API valueToString(UInt value);\n#endif  // if defined(JSON_HAS_INT64)\nJSONCPP_STRING JSON_API valueToString(LargestInt value);\nJSONCPP_STRING JSON_API valueToString(LargestUInt value);\nJSONCPP_STRING JSON_API valueToString(double value);\nJSONCPP_STRING JSON_API valueToString(bool value);\nJSONCPP_STRING JSON_API valueToQuotedString(const char* value);\n\n/// \\brief Output using the StyledStreamWriter.\n/// \\see Json::operator>>()\nJSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root);\n\n}  // namespace Json\n\n#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n#pragma warning(pop)\n#endif  // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)\n\n#endif  // JSON_WRITER_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/writer.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: include/json/assertions.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED\n#define CPPTL_JSON_ASSERTIONS_H_INCLUDED\n\n#include <stdlib.h>\n#include <sstream>\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include \"config.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n\n/** It should not be possible for a maliciously designed file to\n *  cause an abort() or seg-fault, so these macros are used only\n *  for pre-condition violations and internal logic errors.\n */\n#if JSON_USE_EXCEPTION\n\n// @todo <= add detail about condition in exception\n#define JSON_ASSERT(condition)                     \\\n  {                                                \\\n    if (!(condition)) {                            \\\n      Json::throwLogicError(\"assert json failed\"); \\\n    }                                              \\\n  }\n\n#define JSON_FAIL_MESSAGE(message)    \\\n  {                                   \\\n    JSONCPP_OSTRINGSTREAM oss;        \\\n    oss << message;                   \\\n    Json::throwLogicError(oss.str()); \\\n    abort();                          \\\n  }\n\n#else  // JSON_USE_EXCEPTION\n\n#define JSON_ASSERT(condition) assert(condition)\n\n// The call to assert() will show the failure message in debug builds. In\n// release builds we abort, for a core-dump or debugger.\n#define JSON_FAIL_MESSAGE(message)      \\\n  {                                     \\\n    JSONCPP_OSTRINGSTREAM oss;          \\\n    oss << message;                     \\\n    assert(false && oss.str().c_str()); \\\n    abort();                            \\\n  }\n\n#endif\n\n#define JSON_ASSERT_MESSAGE(condition, message) \\\n  if (!(condition)) {                           \\\n    JSON_FAIL_MESSAGE(message);                 \\\n  }\n\n#endif  // CPPTL_JSON_ASSERTIONS_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: include/json/assertions.h\n// //////////////////////////////////////////////////////////////////////\n\n#endif  // ifndef JSON_AMALGATED_H_INCLUDED\n"
  },
  {
    "path": "examples/websocket_client/jsoncpp.cpp",
    "content": "/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).\n/// It is intended to be used with #include \"json/json.h\"\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n/*\nThe JsonCpp library's source code, including accompanying documentation,\ntests and demonstration applications, are licensed under the following\nconditions...\n\nThe author (Baptiste Lepilleur) explicitly disclaims copyright in all\njurisdictions which recognize such a disclaimer. In such jurisdictions,\nthis software is released into the Public Domain.\n\nIn jurisdictions which do not recognize Public Domain property (e.g. Germany as of\n2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is\nreleased under the terms of the MIT License (see below).\n\nIn jurisdictions which recognize Public Domain property, the user of this\nsoftware may choose to accept it either as 1) Public Domain, 2) under the\nconditions of the MIT License (see below), or 3) under the terms of dual\nPublic Domain/MIT License conditions described here, as they choose.\n\nThe MIT License is about as close to Public Domain as a license can get, and is\ndescribed in clear, concise terms at:\n\n   http://en.wikipedia.org/wiki/MIT_License\n\nThe full text of the MIT License follows:\n\n========================================================================\nCopyright (c) 2007-2010 Baptiste Lepilleur\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use, copy,\nmodify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\nBE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n========================================================================\n(END LICENSE TEXT)\n\nThe MIT license is compatible with both the GPL and commercial\nsoftware, affording one all of the rights of Public Domain with the\nminor nuisance of being required to keep the above copyright notice\nand license text in the source code. Note also that by accepting the\nPublic Domain \"license\" you can re-license your copy using whatever\nlicense you like.\n\n*/\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: LICENSE\n// //////////////////////////////////////////////////////////////////////\n\n#include \"json/json.h\"\n\n#ifndef JSON_IS_AMALGAMATION\n#error \"Compile with -I PATH_TO_JSON_DIRECTORY\"\n#endif\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_tool.h\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED\n#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED\n\n/* This header provides common string manipulation support, such as UTF-8,\n * portable conversion from/to string...\n *\n * It is an internal header that must not be exposed.\n */\n\nnamespace Json {\n\n/// Converts a unicode code-point to UTF-8.\nstatic inline JSONCPP_STRING codePointToUTF8(unsigned int cp) {\n  JSONCPP_STRING result;\n\n  // based on description from http://en.wikipedia.org/wiki/UTF-8\n\n  if (cp <= 0x7f) {\n    result.resize(1);\n    result[0] = static_cast<char>(cp);\n  } else if (cp <= 0x7FF) {\n    result.resize(2);\n    result[1] = static_cast<char>(0x80 | (0x3f & cp));\n    result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));\n  } else if (cp <= 0xFFFF) {\n    result.resize(3);\n    result[2] = static_cast<char>(0x80 | (0x3f & cp));\n    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));\n    result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));\n  } else if (cp <= 0x10FFFF) {\n    result.resize(4);\n    result[3] = static_cast<char>(0x80 | (0x3f & cp));\n    result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));\n    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));\n    result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));\n  }\n\n  return result;\n}\n\n/// Returns true if ch is a control character (in range [1,31]).\nstatic inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }\n\nenum {\n  /// Constant that specify the size of the buffer that must be passed to\n  /// uintToString.\n  uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1\n};\n\n// Defines a char buffer for use with uintToString().\ntypedef char UIntToStringBuffer[uintToStringBufferSize];\n\n/** Converts an unsigned integer to string.\n * @param value Unsigned interger to convert to string\n * @param current Input/Output string buffer.\n *        Must have at least uintToStringBufferSize chars free.\n */\nstatic inline void uintToString(LargestUInt value, char*& current) {\n  *--current = 0;\n  do {\n    *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));\n    value /= 10;\n  } while (value != 0);\n}\n\n/** Change ',' to '.' everywhere in buffer.\n *\n * We had a sophisticated way, but it did not work in WinCE.\n * @see https://github.com/open-source-parsers/jsoncpp/pull/9\n */\nstatic inline void fixNumericLocale(char* begin, char* end) {\n  while (begin < end) {\n    if (*begin == ',') {\n      *begin = '.';\n    }\n    ++begin;\n  }\n}\n\n}  // namespace Json {\n\n#endif  // LIB_JSONCPP_JSON_TOOL_H_INCLUDED\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_tool.h\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_reader.cpp\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2011 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include <json/assertions.h>\n#include <json/reader.h>\n#include <json/value.h>\n#include \"json_tool.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n#include <cassert>\n#include <cstdio>\n#include <cstring>\n#include <istream>\n#include <limits>\n#include <memory>\n#include <set>\n#include <sstream>\n#include <utility>\n\n#if defined(_MSC_VER)\n#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500  // VC++ 9.0 and above\n#define snprintf sprintf_s\n#elif _MSC_VER >= 1900  // VC++ 14.0 and above\n#define snprintf std::snprintf\n#else\n#define snprintf _snprintf\n#endif\n#elif defined(__ANDROID__) || defined(__QNXNTO__)\n#define snprintf snprintf\n#elif __cplusplus >= 201103L\n#if !defined(__MINGW32__) && !defined(__CYGWIN__)\n#define snprintf std::snprintf\n#endif\n#endif\n\n#if defined(__QNXNTO__)\n#define sscanf std::sscanf\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400  // VC++ 8.0\n// Disable warning about strdup being deprecated.\n#pragma warning(disable : 4996)\n#endif\n\nstatic int const stackLimit_g = 1000;\nstatic int stackDepth_g = 0;  // see readValue()\n\nnamespace Json {\n\n#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)\ntypedef std::unique_ptr<CharReader> CharReaderPtr;\n#else\ntypedef std::auto_ptr<CharReader> CharReaderPtr;\n#endif\n\n// Implementation of class Features\n// ////////////////////////////////\n\nFeatures::Features() : allowComments_(true), strictRoot_(false), allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}\n\nFeatures Features::all() { return Features(); }\n\nFeatures Features::strictMode() {\n  Features features;\n  features.allowComments_ = false;\n  features.strictRoot_ = true;\n  features.allowDroppedNullPlaceholders_ = false;\n  features.allowNumericKeys_ = false;\n  return features;\n}\n\n// Implementation of class Reader\n// ////////////////////////////////\n\nstatic bool containsNewLine(Reader::Location begin, Reader::Location end) {\n  for (; begin < end; ++begin)\n    if (*begin == '\\n' || *begin == '\\r') return true;\n  return false;\n}\n\n// Class Reader\n// //////////////////////////////////////////////////////////////////\n\nReader::Reader()\n    : errors_(),\n      document_(),\n      begin_(),\n      end_(),\n      current_(),\n      lastValueEnd_(),\n      lastValue_(),\n      commentsBefore_(),\n      features_(Features::all()),\n      collectComments_() {}\n\nReader::Reader(const Features& features)\n    : errors_(),\n      document_(),\n      begin_(),\n      end_(),\n      current_(),\n      lastValueEnd_(),\n      lastValue_(),\n      commentsBefore_(),\n      features_(features),\n      collectComments_() {}\n\nbool Reader::parse(const std::string& document, Value& root, bool collectComments) {\n  JSONCPP_STRING documentCopy(document.data(), document.data() + document.capacity());\n  std::swap(documentCopy, document_);\n  const char* begin = document_.c_str();\n  const char* end = begin + document_.length();\n  return parse(begin, end, root, collectComments);\n}\n\nbool Reader::parse(std::istream& sin, Value& root, bool collectComments) {\n  // std::istream_iterator<char> begin(sin);\n  // std::istream_iterator<char> end;\n  // Those would allow streamed input from a file, if parse() were a\n  // template function.\n\n  // Since JSONCPP_STRING is reference-counted, this at least does not\n  // create an extra copy.\n  JSONCPP_STRING doc;\n  std::getline(sin, doc, (char)EOF);\n  return parse(doc.data(), doc.data() + doc.size(), root, collectComments);\n}\n\nbool Reader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments) {\n  if (!features_.allowComments_) {\n    collectComments = false;\n  }\n\n  begin_ = beginDoc;\n  end_ = endDoc;\n  collectComments_ = collectComments;\n  current_ = begin_;\n  lastValueEnd_ = 0;\n  lastValue_ = 0;\n  commentsBefore_ = \"\";\n  errors_.clear();\n  while (!nodes_.empty()) nodes_.pop();\n  nodes_.push(&root);\n\n  stackDepth_g = 0;  // Yes, this is bad coding, but options are limited.\n  bool successful = readValue();\n  Token token;\n  skipCommentTokens(token);\n  if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter);\n  if (features_.strictRoot_) {\n    if (!root.isArray() && !root.isObject()) {\n      // Set error location to start of doc, ideally should be first token found\n      // in doc\n      token.type_ = tokenError;\n      token.start_ = beginDoc;\n      token.end_ = endDoc;\n      addError(\"A valid JSON document must be either an array or an object value.\", token);\n      return false;\n    }\n  }\n  return successful;\n}\n\nbool Reader::readValue() {\n  // This is a non-reentrant way to support a stackLimit. Terrible!\n  // But this deprecated class has a security problem: Bad input can\n  // cause a seg-fault. This seems like a fair, binary-compatible way\n  // to prevent the problem.\n  if (stackDepth_g >= stackLimit_g) throwRuntimeError(\"Exceeded stackLimit in readValue().\");\n  ++stackDepth_g;\n\n  Token token;\n  skipCommentTokens(token);\n  bool successful = true;\n\n  if (collectComments_ && !commentsBefore_.empty()) {\n    currentValue().setComment(commentsBefore_, commentBefore);\n    commentsBefore_ = \"\";\n  }\n\n  switch (token.type_) {\n    case tokenObjectBegin:\n      successful = readObject(token);\n      currentValue().setOffsetLimit(current_ - begin_);\n      break;\n    case tokenArrayBegin:\n      successful = readArray(token);\n      currentValue().setOffsetLimit(current_ - begin_);\n      break;\n    case tokenNumber:\n      successful = decodeNumber(token);\n      break;\n    case tokenString:\n      successful = decodeString(token);\n      break;\n    case tokenTrue: {\n      Value v(true);\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenFalse: {\n      Value v(false);\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenNull: {\n      Value v;\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenArraySeparator:\n    case tokenObjectEnd:\n    case tokenArrayEnd:\n      if (features_.allowDroppedNullPlaceholders_) {\n        // \"Un-read\" the current token and mark the current value as a null\n        // token.\n        current_--;\n        Value v;\n        currentValue().swapPayload(v);\n        currentValue().setOffsetStart(current_ - begin_ - 1);\n        currentValue().setOffsetLimit(current_ - begin_);\n        break;\n      }  // Else, fall through...\n    default:\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n      return addError(\"Syntax error: value, object or array expected.\", token);\n  }\n\n  if (collectComments_) {\n    lastValueEnd_ = current_;\n    lastValue_ = &currentValue();\n  }\n\n  --stackDepth_g;\n  return successful;\n}\n\nvoid Reader::skipCommentTokens(Token& token) {\n  if (features_.allowComments_) {\n    do {\n      readToken(token);\n    } while (token.type_ == tokenComment);\n  } else {\n    readToken(token);\n  }\n}\n\nbool Reader::readToken(Token& token) {\n  skipSpaces();\n  token.start_ = current_;\n  Char c = getNextChar();\n  bool ok = true;\n  switch (c) {\n    case '{':\n      token.type_ = tokenObjectBegin;\n      break;\n    case '}':\n      token.type_ = tokenObjectEnd;\n      break;\n    case '[':\n      token.type_ = tokenArrayBegin;\n      break;\n    case ']':\n      token.type_ = tokenArrayEnd;\n      break;\n    case '\"':\n      token.type_ = tokenString;\n      ok = readString();\n      break;\n    case '/':\n      token.type_ = tokenComment;\n      ok = readComment();\n      break;\n    case '0':\n    case '1':\n    case '2':\n    case '3':\n    case '4':\n    case '5':\n    case '6':\n    case '7':\n    case '8':\n    case '9':\n    case '-':\n      token.type_ = tokenNumber;\n      readNumber();\n      break;\n    case 't':\n      token.type_ = tokenTrue;\n      ok = match(\"rue\", 3);\n      break;\n    case 'f':\n      token.type_ = tokenFalse;\n      ok = match(\"alse\", 4);\n      break;\n    case 'n':\n      token.type_ = tokenNull;\n      ok = match(\"ull\", 3);\n      break;\n    case ',':\n      token.type_ = tokenArraySeparator;\n      break;\n    case ':':\n      token.type_ = tokenMemberSeparator;\n      break;\n    case 0:\n      token.type_ = tokenEndOfStream;\n      break;\n    default:\n      ok = false;\n      break;\n  }\n  if (!ok) token.type_ = tokenError;\n  token.end_ = current_;\n  return true;\n}\n\nvoid Reader::skipSpaces() {\n  while (current_ != end_) {\n    Char c = *current_;\n    if (c == ' ' || c == '\\t' || c == '\\r' || c == '\\n')\n      ++current_;\n    else\n      break;\n  }\n}\n\nbool Reader::match(Location pattern, int patternLength) {\n  if (end_ - current_ < patternLength) return false;\n  int index = patternLength;\n  while (index--)\n    if (current_[index] != pattern[index]) return false;\n  current_ += patternLength;\n  return true;\n}\n\nbool Reader::readComment() {\n  Location commentBegin = current_ - 1;\n  Char c = getNextChar();\n  bool successful = false;\n  if (c == '*')\n    successful = readCStyleComment();\n  else if (c == '/')\n    successful = readCppStyleComment();\n  if (!successful) return false;\n\n  if (collectComments_) {\n    CommentPlacement placement = commentBefore;\n    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {\n      if (c != '*' || !containsNewLine(commentBegin, current_)) placement = commentAfterOnSameLine;\n    }\n\n    addComment(commentBegin, current_, placement);\n  }\n  return true;\n}\n\nstatic JSONCPP_STRING normalizeEOL(Reader::Location begin, Reader::Location end) {\n  JSONCPP_STRING normalized;\n  normalized.reserve(static_cast<size_t>(end - begin));\n  Reader::Location current = begin;\n  while (current != end) {\n    char c = *current++;\n    if (c == '\\r') {\n      if (current != end && *current == '\\n')\n        // convert dos EOL\n        ++current;\n      // convert Mac EOL\n      normalized += '\\n';\n    } else {\n      normalized += c;\n    }\n  }\n  return normalized;\n}\n\nvoid Reader::addComment(Location begin, Location end, CommentPlacement placement) {\n  assert(collectComments_);\n  const JSONCPP_STRING& normalized = normalizeEOL(begin, end);\n  if (placement == commentAfterOnSameLine) {\n    assert(lastValue_ != 0);\n    lastValue_->setComment(normalized, placement);\n  } else {\n    commentsBefore_ += normalized;\n  }\n}\n\nbool Reader::readCStyleComment() {\n  while (current_ != end_) {\n    Char c = getNextChar();\n    if (c == '*' && *current_ == '/') break;\n  }\n  return getNextChar() == '/';\n}\n\nbool Reader::readCppStyleComment() {\n  while (current_ != end_) {\n    Char c = getNextChar();\n    if (c == '\\n') break;\n    if (c == '\\r') {\n      // Consume DOS EOL. It will be normalized in addComment.\n      if (current_ != end_ && *current_ == '\\n') getNextChar();\n      // Break on Moc OS 9 EOL.\n      break;\n    }\n  }\n  return true;\n}\n\nvoid Reader::readNumber() {\n  const char* p = current_;\n  char c = '0';  // stopgap for already consumed character\n  // integral part\n  while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : '\\0';\n  // fractional part\n  if (c == '.') {\n    c = (current_ = p) < end_ ? *p++ : '\\0';\n    while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : '\\0';\n  }\n  // exponential part\n  if (c == 'e' || c == 'E') {\n    c = (current_ = p) < end_ ? *p++ : '\\0';\n    if (c == '+' || c == '-') c = (current_ = p) < end_ ? *p++ : '\\0';\n    while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : '\\0';\n  }\n}\n\nbool Reader::readString() {\n  Char c = '\\0';\n  while (current_ != end_) {\n    c = getNextChar();\n    if (c == '\\\\')\n      getNextChar();\n    else if (c == '\"')\n      break;\n  }\n  return c == '\"';\n}\n\nbool Reader::readObject(Token& tokenStart) {\n  Token tokenName;\n  JSONCPP_STRING name;\n  Value init(objectValue);\n  currentValue().swapPayload(init);\n  currentValue().setOffsetStart(tokenStart.start_ - begin_);\n  while (readToken(tokenName)) {\n    bool initialTokenOk = true;\n    while (tokenName.type_ == tokenComment && initialTokenOk) initialTokenOk = readToken(tokenName);\n    if (!initialTokenOk) break;\n    if (tokenName.type_ == tokenObjectEnd && name.empty())  // empty object\n      return true;\n    name = \"\";\n    if (tokenName.type_ == tokenString) {\n      if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd);\n    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {\n      Value numberName;\n      if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd);\n      name = JSONCPP_STRING(numberName.asCString());\n    } else {\n      break;\n    }\n\n    Token colon;\n    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {\n      return addErrorAndRecover(\"Missing ':' after object member name\", colon, tokenObjectEnd);\n    }\n    Value& value = currentValue()[name];\n    nodes_.push(&value);\n    bool ok = readValue();\n    nodes_.pop();\n    if (!ok)  // error already set\n      return recoverFromError(tokenObjectEnd);\n\n    Token comma;\n    if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) {\n      return addErrorAndRecover(\"Missing ',' or '}' in object declaration\", comma, tokenObjectEnd);\n    }\n    bool finalizeTokenOk = true;\n    while (comma.type_ == tokenComment && finalizeTokenOk) finalizeTokenOk = readToken(comma);\n    if (comma.type_ == tokenObjectEnd) return true;\n  }\n  return addErrorAndRecover(\"Missing '}' or object member name\", tokenName, tokenObjectEnd);\n}\n\nbool Reader::readArray(Token& tokenStart) {\n  Value init(arrayValue);\n  currentValue().swapPayload(init);\n  currentValue().setOffsetStart(tokenStart.start_ - begin_);\n  skipSpaces();\n  if (*current_ == ']')  // empty array\n  {\n    Token endArray;\n    readToken(endArray);\n    return true;\n  }\n  int index = 0;\n  for (;;) {\n    Value& value = currentValue()[index++];\n    nodes_.push(&value);\n    bool ok = readValue();\n    nodes_.pop();\n    if (!ok)  // error already set\n      return recoverFromError(tokenArrayEnd);\n\n    Token token;\n    // Accept Comment after last item in the array.\n    ok = readToken(token);\n    while (token.type_ == tokenComment && ok) {\n      ok = readToken(token);\n    }\n    bool badTokenType = (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);\n    if (!ok || badTokenType) {\n      return addErrorAndRecover(\"Missing ',' or ']' in array declaration\", token, tokenArrayEnd);\n    }\n    if (token.type_ == tokenArrayEnd) break;\n  }\n  return true;\n}\n\nbool Reader::decodeNumber(Token& token) {\n  Value decoded;\n  if (!decodeNumber(token, decoded)) return false;\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool Reader::decodeNumber(Token& token, Value& decoded) {\n  // Attempts to parse the number as an integer. If the number is\n  // larger than the maximum supported value of an integer then\n  // we decode the number as a double.\n  Location current = token.start_;\n  bool isNegative = *current == '-';\n  if (isNegative) ++current;\n  // TODO: Help the compiler do the div and mod at compile time or get rid of them.\n  Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 : Value::maxLargestUInt;\n  Value::LargestUInt threshold = maxIntegerValue / 10;\n  Value::LargestUInt value = 0;\n  while (current < token.end_) {\n    Char c = *current++;\n    if (c < '0' || c > '9') return decodeDouble(token, decoded);\n    Value::UInt digit(static_cast<Value::UInt>(c - '0'));\n    if (value >= threshold) {\n      // We've hit or exceeded the max value divided by 10 (rounded down). If\n      // a) we've only just touched the limit, b) this is the last digit, and\n      // c) it's small enough to fit in that rounding delta, we're okay.\n      // Otherwise treat this number as a double to avoid overflow.\n      if (value > threshold || current != token.end_ || digit > maxIntegerValue % 10) {\n        return decodeDouble(token, decoded);\n      }\n    }\n    value = value * 10 + digit;\n  }\n  if (isNegative && value == maxIntegerValue)\n    decoded = Value::minLargestInt;\n  else if (isNegative)\n    decoded = -Value::LargestInt(value);\n  else if (value <= Value::LargestUInt(Value::maxInt))\n    decoded = Value::LargestInt(value);\n  else\n    decoded = value;\n  return true;\n}\n\nbool Reader::decodeDouble(Token& token) {\n  Value decoded;\n  if (!decodeDouble(token, decoded)) return false;\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool Reader::decodeDouble(Token& token, Value& decoded) {\n  double value = 0;\n  JSONCPP_STRING buffer(token.start_, token.end_);\n  JSONCPP_ISTRINGSTREAM is(buffer);\n  if (!(is >> value)) return addError(\"'\" + JSONCPP_STRING(token.start_, token.end_) + \"' is not a number.\", token);\n  decoded = value;\n  return true;\n}\n\nbool Reader::decodeString(Token& token) {\n  JSONCPP_STRING decoded_string;\n  if (!decodeString(token, decoded_string)) return false;\n  Value decoded(decoded_string);\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) {\n  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));\n  Location current = token.start_ + 1;  // skip '\"'\n  Location end = token.end_ - 1;        // do not include '\"'\n  while (current != end) {\n    Char c = *current++;\n    if (c == '\"')\n      break;\n    else if (c == '\\\\') {\n      if (current == end) return addError(\"Empty escape sequence in string\", token, current);\n      Char escape = *current++;\n      switch (escape) {\n        case '\"':\n          decoded += '\"';\n          break;\n        case '/':\n          decoded += '/';\n          break;\n        case '\\\\':\n          decoded += '\\\\';\n          break;\n        case 'b':\n          decoded += '\\b';\n          break;\n        case 'f':\n          decoded += '\\f';\n          break;\n        case 'n':\n          decoded += '\\n';\n          break;\n        case 'r':\n          decoded += '\\r';\n          break;\n        case 't':\n          decoded += '\\t';\n          break;\n        case 'u': {\n          unsigned int unicode;\n          if (!decodeUnicodeCodePoint(token, current, end, unicode)) return false;\n          decoded += codePointToUTF8(unicode);\n        } break;\n        default:\n          return addError(\"Bad escape sequence in string\", token, current);\n      }\n    } else {\n      decoded += c;\n    }\n  }\n  return true;\n}\n\nbool Reader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode) {\n  if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false;\n  if (unicode >= 0xD800 && unicode <= 0xDBFF) {\n    // surrogate pairs\n    if (end - current < 6) return addError(\"additional six characters expected to parse unicode surrogate pair.\", token, current);\n    unsigned int surrogatePair;\n    if (*(current++) == '\\\\' && *(current++) == 'u') {\n      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {\n        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);\n      } else\n        return false;\n    } else\n      return addError(\n          \"expecting another \\\\u token to begin the second half of \"\n          \"a unicode surrogate pair\",\n          token, current);\n  }\n  return true;\n}\n\nbool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& ret_unicode) {\n  if (end - current < 4) return addError(\"Bad unicode escape sequence in string: four digits expected.\", token, current);\n  int unicode = 0;\n  for (int index = 0; index < 4; ++index) {\n    Char c = *current++;\n    unicode *= 16;\n    if (c >= '0' && c <= '9')\n      unicode += c - '0';\n    else if (c >= 'a' && c <= 'f')\n      unicode += c - 'a' + 10;\n    else if (c >= 'A' && c <= 'F')\n      unicode += c - 'A' + 10;\n    else\n      return addError(\"Bad unicode escape sequence in string: hexadecimal digit expected.\", token, current);\n  }\n  ret_unicode = static_cast<unsigned int>(unicode);\n  return true;\n}\n\nbool Reader::addError(const JSONCPP_STRING& message, Token& token, Location extra) {\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = extra;\n  errors_.push_back(info);\n  return false;\n}\n\nbool Reader::recoverFromError(TokenType skipUntilToken) {\n  size_t const errorCount = errors_.size();\n  Token skip;\n  for (;;) {\n    if (!readToken(skip)) errors_.resize(errorCount);  // discard errors caused by recovery\n    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) break;\n  }\n  errors_.resize(errorCount);\n  return false;\n}\n\nbool Reader::addErrorAndRecover(const JSONCPP_STRING& message, Token& token, TokenType skipUntilToken) {\n  addError(message, token);\n  return recoverFromError(skipUntilToken);\n}\n\nValue& Reader::currentValue() { return *(nodes_.top()); }\n\nReader::Char Reader::getNextChar() {\n  if (current_ == end_) return 0;\n  return *current_++;\n}\n\nvoid Reader::getLocationLineAndColumn(Location location, int& line, int& column) const {\n  Location current = begin_;\n  Location lastLineStart = current;\n  line = 0;\n  while (current < location && current != end_) {\n    Char c = *current++;\n    if (c == '\\r') {\n      if (*current == '\\n') ++current;\n      lastLineStart = current;\n      ++line;\n    } else if (c == '\\n') {\n      lastLineStart = current;\n      ++line;\n    }\n  }\n  // column & line start at 1\n  column = int(location - lastLineStart) + 1;\n  ++line;\n}\n\nJSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const {\n  int line, column;\n  getLocationLineAndColumn(location, line, column);\n  char buffer[18 + 16 + 16 + 1];\n  snprintf(buffer, sizeof(buffer), \"Line %d, Column %d\", line, column);\n  return buffer;\n}\n\n// Deprecated. Preserved for backward compatibility\nJSONCPP_STRING Reader::getFormatedErrorMessages() const { return getFormattedErrorMessages(); }\n\nJSONCPP_STRING Reader::getFormattedErrorMessages() const {\n  JSONCPP_STRING formattedMessage;\n  for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) {\n    const ErrorInfo& error = *itError;\n    formattedMessage += \"* \" + getLocationLineAndColumn(error.token_.start_) + \"\\n\";\n    formattedMessage += \"  \" + error.message_ + \"\\n\";\n    if (error.extra_) formattedMessage += \"See \" + getLocationLineAndColumn(error.extra_) + \" for detail.\\n\";\n  }\n  return formattedMessage;\n}\n\nstd::vector<Reader::StructuredError> Reader::getStructuredErrors() const {\n  std::vector<Reader::StructuredError> allErrors;\n  for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) {\n    const ErrorInfo& error = *itError;\n    Reader::StructuredError structured;\n    structured.offset_start = error.token_.start_ - begin_;\n    structured.offset_limit = error.token_.end_ - begin_;\n    structured.message = error.message_;\n    allErrors.push_back(structured);\n  }\n  return allErrors;\n}\n\nbool Reader::pushError(const Value& value, const JSONCPP_STRING& message) {\n  ptrdiff_t const length = end_ - begin_;\n  if (value.getOffsetStart() > length || value.getOffsetLimit() > length) return false;\n  Token token;\n  token.type_ = tokenError;\n  token.start_ = begin_ + value.getOffsetStart();\n  token.end_ = end_ + value.getOffsetLimit();\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = 0;\n  errors_.push_back(info);\n  return true;\n}\n\nbool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) {\n  ptrdiff_t const length = end_ - begin_;\n  if (value.getOffsetStart() > length || value.getOffsetLimit() > length || extra.getOffsetLimit() > length) return false;\n  Token token;\n  token.type_ = tokenError;\n  token.start_ = begin_ + value.getOffsetStart();\n  token.end_ = begin_ + value.getOffsetLimit();\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = begin_ + extra.getOffsetStart();\n  errors_.push_back(info);\n  return true;\n}\n\nbool Reader::good() const { return !errors_.size(); }\n\n// exact copy of Features\nclass OurFeatures {\n public:\n  static OurFeatures all();\n  bool allowComments_;\n  bool strictRoot_;\n  bool allowDroppedNullPlaceholders_;\n  bool allowNumericKeys_;\n  bool allowSingleQuotes_;\n  bool failIfExtra_;\n  bool rejectDupKeys_;\n  bool allowSpecialFloats_;\n  int stackLimit_;\n};  // OurFeatures\n\n// exact copy of Implementation of class Features\n// ////////////////////////////////\n\nOurFeatures OurFeatures::all() { return OurFeatures(); }\n\n// Implementation of class Reader\n// ////////////////////////////////\n\n// exact copy of Reader, renamed to OurReader\nclass OurReader {\n public:\n  typedef char Char;\n  typedef const Char* Location;\n  struct StructuredError {\n    ptrdiff_t offset_start;\n    ptrdiff_t offset_limit;\n    JSONCPP_STRING message;\n  };\n\n  OurReader(OurFeatures const& features);\n  bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true);\n  JSONCPP_STRING getFormattedErrorMessages() const;\n  std::vector<StructuredError> getStructuredErrors() const;\n  bool pushError(const Value& value, const JSONCPP_STRING& message);\n  bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra);\n  bool good() const;\n\n private:\n  OurReader(OurReader const&);       // no impl\n  void operator=(OurReader const&);  // no impl\n\n  enum TokenType {\n    tokenEndOfStream = 0,\n    tokenObjectBegin,\n    tokenObjectEnd,\n    tokenArrayBegin,\n    tokenArrayEnd,\n    tokenString,\n    tokenNumber,\n    tokenTrue,\n    tokenFalse,\n    tokenNull,\n    tokenNaN,\n    tokenPosInf,\n    tokenNegInf,\n    tokenArraySeparator,\n    tokenMemberSeparator,\n    tokenComment,\n    tokenError\n  };\n\n  class Token {\n   public:\n    TokenType type_;\n    Location start_;\n    Location end_;\n  };\n\n  class ErrorInfo {\n   public:\n    Token token_;\n    JSONCPP_STRING message_;\n    Location extra_;\n  };\n\n  typedef std::deque<ErrorInfo> Errors;\n\n  bool readToken(Token& token);\n  void skipSpaces();\n  bool match(Location pattern, int patternLength);\n  bool readComment();\n  bool readCStyleComment();\n  bool readCppStyleComment();\n  bool readString();\n  bool readStringSingleQuote();\n  bool readNumber(bool checkInf);\n  bool readValue();\n  bool readObject(Token& token);\n  bool readArray(Token& token);\n  bool decodeNumber(Token& token);\n  bool decodeNumber(Token& token, Value& decoded);\n  bool decodeString(Token& token);\n  bool decodeString(Token& token, JSONCPP_STRING& decoded);\n  bool decodeDouble(Token& token);\n  bool decodeDouble(Token& token, Value& decoded);\n  bool decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode);\n  bool decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& unicode);\n  bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0);\n  bool recoverFromError(TokenType skipUntilToken);\n  bool addErrorAndRecover(const JSONCPP_STRING& message, Token& token, TokenType skipUntilToken);\n  void skipUntilSpace();\n  Value& currentValue();\n  Char getNextChar();\n  void getLocationLineAndColumn(Location location, int& line, int& column) const;\n  JSONCPP_STRING getLocationLineAndColumn(Location location) const;\n  void addComment(Location begin, Location end, CommentPlacement placement);\n  void skipCommentTokens(Token& token);\n\n  typedef std::stack<Value*> Nodes;\n  Nodes nodes_;\n  Errors errors_;\n  JSONCPP_STRING document_;\n  Location begin_;\n  Location end_;\n  Location current_;\n  Location lastValueEnd_;\n  Value* lastValue_;\n  JSONCPP_STRING commentsBefore_;\n  int stackDepth_;\n\n  OurFeatures const features_;\n  bool collectComments_;\n};  // OurReader\n\n// complete copy of Read impl, for OurReader\n\nOurReader::OurReader(OurFeatures const& features)\n    : errors_(),\n      document_(),\n      begin_(),\n      end_(),\n      current_(),\n      lastValueEnd_(),\n      lastValue_(),\n      commentsBefore_(),\n      stackDepth_(0),\n      features_(features),\n      collectComments_() {}\n\nbool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments) {\n  if (!features_.allowComments_) {\n    collectComments = false;\n  }\n\n  begin_ = beginDoc;\n  end_ = endDoc;\n  collectComments_ = collectComments;\n  current_ = begin_;\n  lastValueEnd_ = 0;\n  lastValue_ = 0;\n  commentsBefore_ = \"\";\n  errors_.clear();\n  while (!nodes_.empty()) nodes_.pop();\n  nodes_.push(&root);\n\n  stackDepth_ = 0;\n  bool successful = readValue();\n  Token token;\n  skipCommentTokens(token);\n  if (features_.failIfExtra_) {\n    if (token.type_ != tokenError && token.type_ != tokenEndOfStream) {\n      addError(\"Extra non-whitespace after JSON value.\", token);\n      return false;\n    }\n  }\n  if (collectComments_ && !commentsBefore_.empty()) root.setComment(commentsBefore_, commentAfter);\n  if (features_.strictRoot_) {\n    if (!root.isArray() && !root.isObject()) {\n      // Set error location to start of doc, ideally should be first token found\n      // in doc\n      token.type_ = tokenError;\n      token.start_ = beginDoc;\n      token.end_ = endDoc;\n      addError(\"A valid JSON document must be either an array or an object value.\", token);\n      return false;\n    }\n  }\n  return successful;\n}\n\nbool OurReader::readValue() {\n  if (stackDepth_ >= features_.stackLimit_) throwRuntimeError(\"Exceeded stackLimit in readValue().\");\n  ++stackDepth_;\n  Token token;\n  skipCommentTokens(token);\n  bool successful = true;\n\n  if (collectComments_ && !commentsBefore_.empty()) {\n    currentValue().setComment(commentsBefore_, commentBefore);\n    commentsBefore_ = \"\";\n  }\n\n  switch (token.type_) {\n    case tokenObjectBegin:\n      successful = readObject(token);\n      currentValue().setOffsetLimit(current_ - begin_);\n      break;\n    case tokenArrayBegin:\n      successful = readArray(token);\n      currentValue().setOffsetLimit(current_ - begin_);\n      break;\n    case tokenNumber:\n      successful = decodeNumber(token);\n      break;\n    case tokenString:\n      successful = decodeString(token);\n      break;\n    case tokenTrue: {\n      Value v(true);\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenFalse: {\n      Value v(false);\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenNull: {\n      Value v;\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenNaN: {\n      Value v(std::numeric_limits<double>::quiet_NaN());\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenPosInf: {\n      Value v(std::numeric_limits<double>::infinity());\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenNegInf: {\n      Value v(-std::numeric_limits<double>::infinity());\n      currentValue().swapPayload(v);\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n    } break;\n    case tokenArraySeparator:\n    case tokenObjectEnd:\n    case tokenArrayEnd:\n      if (features_.allowDroppedNullPlaceholders_) {\n        // \"Un-read\" the current token and mark the current value as a null\n        // token.\n        current_--;\n        Value v;\n        currentValue().swapPayload(v);\n        currentValue().setOffsetStart(current_ - begin_ - 1);\n        currentValue().setOffsetLimit(current_ - begin_);\n        break;\n      }  // else, fall through ...\n    default:\n      currentValue().setOffsetStart(token.start_ - begin_);\n      currentValue().setOffsetLimit(token.end_ - begin_);\n      return addError(\"Syntax error: value, object or array expected.\", token);\n  }\n\n  if (collectComments_) {\n    lastValueEnd_ = current_;\n    lastValue_ = &currentValue();\n  }\n\n  --stackDepth_;\n  return successful;\n}\n\nvoid OurReader::skipCommentTokens(Token& token) {\n  if (features_.allowComments_) {\n    do {\n      readToken(token);\n    } while (token.type_ == tokenComment);\n  } else {\n    readToken(token);\n  }\n}\n\nbool OurReader::readToken(Token& token) {\n  skipSpaces();\n  token.start_ = current_;\n  Char c = getNextChar();\n  bool ok = true;\n  switch (c) {\n    case '{':\n      token.type_ = tokenObjectBegin;\n      break;\n    case '}':\n      token.type_ = tokenObjectEnd;\n      break;\n    case '[':\n      token.type_ = tokenArrayBegin;\n      break;\n    case ']':\n      token.type_ = tokenArrayEnd;\n      break;\n    case '\"':\n      token.type_ = tokenString;\n      ok = readString();\n      break;\n    case '\\'':\n      if (features_.allowSingleQuotes_) {\n        token.type_ = tokenString;\n        ok = readStringSingleQuote();\n        break;\n      }  // else continue\n    case '/':\n      token.type_ = tokenComment;\n      ok = readComment();\n      break;\n    case '0':\n    case '1':\n    case '2':\n    case '3':\n    case '4':\n    case '5':\n    case '6':\n    case '7':\n    case '8':\n    case '9':\n      token.type_ = tokenNumber;\n      readNumber(false);\n      break;\n    case '-':\n      if (readNumber(true)) {\n        token.type_ = tokenNumber;\n      } else {\n        token.type_ = tokenNegInf;\n        ok = features_.allowSpecialFloats_ && match(\"nfinity\", 7);\n      }\n      break;\n    case 't':\n      token.type_ = tokenTrue;\n      ok = match(\"rue\", 3);\n      break;\n    case 'f':\n      token.type_ = tokenFalse;\n      ok = match(\"alse\", 4);\n      break;\n    case 'n':\n      token.type_ = tokenNull;\n      ok = match(\"ull\", 3);\n      break;\n    case 'N':\n      if (features_.allowSpecialFloats_) {\n        token.type_ = tokenNaN;\n        ok = match(\"aN\", 2);\n      } else {\n        ok = false;\n      }\n      break;\n    case 'I':\n      if (features_.allowSpecialFloats_) {\n        token.type_ = tokenPosInf;\n        ok = match(\"nfinity\", 7);\n      } else {\n        ok = false;\n      }\n      break;\n    case ',':\n      token.type_ = tokenArraySeparator;\n      break;\n    case ':':\n      token.type_ = tokenMemberSeparator;\n      break;\n    case 0:\n      token.type_ = tokenEndOfStream;\n      break;\n    default:\n      ok = false;\n      break;\n  }\n  if (!ok) token.type_ = tokenError;\n  token.end_ = current_;\n  return true;\n}\n\nvoid OurReader::skipSpaces() {\n  while (current_ != end_) {\n    Char c = *current_;\n    if (c == ' ' || c == '\\t' || c == '\\r' || c == '\\n')\n      ++current_;\n    else\n      break;\n  }\n}\n\nbool OurReader::match(Location pattern, int patternLength) {\n  if (end_ - current_ < patternLength) return false;\n  int index = patternLength;\n  while (index--)\n    if (current_[index] != pattern[index]) return false;\n  current_ += patternLength;\n  return true;\n}\n\nbool OurReader::readComment() {\n  Location commentBegin = current_ - 1;\n  Char c = getNextChar();\n  bool successful = false;\n  if (c == '*')\n    successful = readCStyleComment();\n  else if (c == '/')\n    successful = readCppStyleComment();\n  if (!successful) return false;\n\n  if (collectComments_) {\n    CommentPlacement placement = commentBefore;\n    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {\n      if (c != '*' || !containsNewLine(commentBegin, current_)) placement = commentAfterOnSameLine;\n    }\n\n    addComment(commentBegin, current_, placement);\n  }\n  return true;\n}\n\nvoid OurReader::addComment(Location begin, Location end, CommentPlacement placement) {\n  assert(collectComments_);\n  const JSONCPP_STRING& normalized = normalizeEOL(begin, end);\n  if (placement == commentAfterOnSameLine) {\n    assert(lastValue_ != 0);\n    lastValue_->setComment(normalized, placement);\n  } else {\n    commentsBefore_ += normalized;\n  }\n}\n\nbool OurReader::readCStyleComment() {\n  while (current_ != end_) {\n    Char c = getNextChar();\n    if (c == '*' && *current_ == '/') break;\n  }\n  return getNextChar() == '/';\n}\n\nbool OurReader::readCppStyleComment() {\n  while (current_ != end_) {\n    Char c = getNextChar();\n    if (c == '\\n') break;\n    if (c == '\\r') {\n      // Consume DOS EOL. It will be normalized in addComment.\n      if (current_ != end_ && *current_ == '\\n') getNextChar();\n      // Break on Moc OS 9 EOL.\n      break;\n    }\n  }\n  return true;\n}\n\nbool OurReader::readNumber(bool checkInf) {\n  const char* p = current_;\n  if (checkInf && p != end_ && *p == 'I') {\n    current_ = ++p;\n    return false;\n  }\n  char c = '0';  // stopgap for already consumed character\n  // integral part\n  while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : '\\0';\n  // fractional part\n  if (c == '.') {\n    c = (current_ = p) < end_ ? *p++ : '\\0';\n    while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : '\\0';\n  }\n  // exponential part\n  if (c == 'e' || c == 'E') {\n    c = (current_ = p) < end_ ? *p++ : '\\0';\n    if (c == '+' || c == '-') c = (current_ = p) < end_ ? *p++ : '\\0';\n    while (c >= '0' && c <= '9') c = (current_ = p) < end_ ? *p++ : '\\0';\n  }\n  return true;\n}\nbool OurReader::readString() {\n  Char c = 0;\n  while (current_ != end_) {\n    c = getNextChar();\n    if (c == '\\\\')\n      getNextChar();\n    else if (c == '\"')\n      break;\n  }\n  return c == '\"';\n}\n\nbool OurReader::readStringSingleQuote() {\n  Char c = 0;\n  while (current_ != end_) {\n    c = getNextChar();\n    if (c == '\\\\')\n      getNextChar();\n    else if (c == '\\'')\n      break;\n  }\n  return c == '\\'';\n}\n\nbool OurReader::readObject(Token& tokenStart) {\n  Token tokenName;\n  JSONCPP_STRING name;\n  Value init(objectValue);\n  currentValue().swapPayload(init);\n  currentValue().setOffsetStart(tokenStart.start_ - begin_);\n  while (readToken(tokenName)) {\n    bool initialTokenOk = true;\n    while (tokenName.type_ == tokenComment && initialTokenOk) initialTokenOk = readToken(tokenName);\n    if (!initialTokenOk) break;\n    if (tokenName.type_ == tokenObjectEnd && name.empty())  // empty object\n      return true;\n    name = \"\";\n    if (tokenName.type_ == tokenString) {\n      if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd);\n    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {\n      Value numberName;\n      if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd);\n      name = numberName.asString();\n    } else {\n      break;\n    }\n\n    Token colon;\n    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {\n      return addErrorAndRecover(\"Missing ':' after object member name\", colon, tokenObjectEnd);\n    }\n    if (name.length() >= (1U << 30)) throwRuntimeError(\"keylength >= 2^30\");\n    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {\n      JSONCPP_STRING msg = \"Duplicate key: '\" + name + \"'\";\n      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);\n    }\n    Value& value = currentValue()[name];\n    nodes_.push(&value);\n    bool ok = readValue();\n    nodes_.pop();\n    if (!ok)  // error already set\n      return recoverFromError(tokenObjectEnd);\n\n    Token comma;\n    if (!readToken(comma) || (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment)) {\n      return addErrorAndRecover(\"Missing ',' or '}' in object declaration\", comma, tokenObjectEnd);\n    }\n    bool finalizeTokenOk = true;\n    while (comma.type_ == tokenComment && finalizeTokenOk) finalizeTokenOk = readToken(comma);\n    if (comma.type_ == tokenObjectEnd) return true;\n  }\n  return addErrorAndRecover(\"Missing '}' or object member name\", tokenName, tokenObjectEnd);\n}\n\nbool OurReader::readArray(Token& tokenStart) {\n  Value init(arrayValue);\n  currentValue().swapPayload(init);\n  currentValue().setOffsetStart(tokenStart.start_ - begin_);\n  skipSpaces();\n  if (*current_ == ']')  // empty array\n  {\n    Token endArray;\n    readToken(endArray);\n    return true;\n  }\n  int index = 0;\n  for (;;) {\n    Value& value = currentValue()[index++];\n    nodes_.push(&value);\n    bool ok = readValue();\n    nodes_.pop();\n    if (!ok)  // error already set\n      return recoverFromError(tokenArrayEnd);\n\n    Token token;\n    // Accept Comment after last item in the array.\n    ok = readToken(token);\n    while (token.type_ == tokenComment && ok) {\n      ok = readToken(token);\n    }\n    bool badTokenType = (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);\n    if (!ok || badTokenType) {\n      return addErrorAndRecover(\"Missing ',' or ']' in array declaration\", token, tokenArrayEnd);\n    }\n    if (token.type_ == tokenArrayEnd) break;\n  }\n  return true;\n}\n\nbool OurReader::decodeNumber(Token& token) {\n  Value decoded;\n  if (!decodeNumber(token, decoded)) return false;\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool OurReader::decodeNumber(Token& token, Value& decoded) {\n  // Attempts to parse the number as an integer. If the number is\n  // larger than the maximum supported value of an integer then\n  // we decode the number as a double.\n  Location current = token.start_;\n  bool isNegative = *current == '-';\n  if (isNegative) ++current;\n  // TODO: Help the compiler do the div and mod at compile time or get rid of them.\n  Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) : Value::maxLargestUInt;\n  Value::LargestUInt threshold = maxIntegerValue / 10;\n  Value::LargestUInt value = 0;\n  while (current < token.end_) {\n    Char c = *current++;\n    if (c < '0' || c > '9') return decodeDouble(token, decoded);\n    Value::UInt digit(static_cast<Value::UInt>(c - '0'));\n    if (value >= threshold) {\n      // We've hit or exceeded the max value divided by 10 (rounded down). If\n      // a) we've only just touched the limit, b) this is the last digit, and\n      // c) it's small enough to fit in that rounding delta, we're okay.\n      // Otherwise treat this number as a double to avoid overflow.\n      if (value > threshold || current != token.end_ || digit > maxIntegerValue % 10) {\n        return decodeDouble(token, decoded);\n      }\n    }\n    value = value * 10 + digit;\n  }\n  if (isNegative)\n    decoded = -Value::LargestInt(value);\n  else if (value <= Value::LargestUInt(Value::maxInt))\n    decoded = Value::LargestInt(value);\n  else\n    decoded = value;\n  return true;\n}\n\nbool OurReader::decodeDouble(Token& token) {\n  Value decoded;\n  if (!decodeDouble(token, decoded)) return false;\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool OurReader::decodeDouble(Token& token, Value& decoded) {\n  double value = 0;\n  const int bufferSize = 32;\n  int count;\n  ptrdiff_t const length = token.end_ - token.start_;\n\n  // Sanity check to avoid buffer overflow exploits.\n  if (length < 0) {\n    return addError(\"Unable to parse token length\", token);\n  }\n  size_t const ulength = static_cast<size_t>(length);\n\n  // Avoid using a string constant for the format control string given to\n  // sscanf, as this can cause hard to debug crashes on OS X. See here for more\n  // info:\n  //\n  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html\n  char format[] = \"%lf\";\n\n  if (length <= bufferSize) {\n    Char buffer[bufferSize + 1];\n    memcpy(buffer, token.start_, ulength);\n    buffer[length] = 0;\n    count = sscanf(buffer, format, &value);\n  } else {\n    JSONCPP_STRING buffer(token.start_, token.end_);\n    count = sscanf(buffer.c_str(), format, &value);\n  }\n\n  if (count != 1) return addError(\"'\" + JSONCPP_STRING(token.start_, token.end_) + \"' is not a number.\", token);\n  decoded = value;\n  return true;\n}\n\nbool OurReader::decodeString(Token& token) {\n  JSONCPP_STRING decoded_string;\n  if (!decodeString(token, decoded_string)) return false;\n  Value decoded(decoded_string);\n  currentValue().swapPayload(decoded);\n  currentValue().setOffsetStart(token.start_ - begin_);\n  currentValue().setOffsetLimit(token.end_ - begin_);\n  return true;\n}\n\nbool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) {\n  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));\n  Location current = token.start_ + 1;  // skip '\"'\n  Location end = token.end_ - 1;        // do not include '\"'\n  while (current != end) {\n    Char c = *current++;\n    if (c == '\"')\n      break;\n    else if (c == '\\\\') {\n      if (current == end) return addError(\"Empty escape sequence in string\", token, current);\n      Char escape = *current++;\n      switch (escape) {\n        case '\"':\n          decoded += '\"';\n          break;\n        case '/':\n          decoded += '/';\n          break;\n        case '\\\\':\n          decoded += '\\\\';\n          break;\n        case 'b':\n          decoded += '\\b';\n          break;\n        case 'f':\n          decoded += '\\f';\n          break;\n        case 'n':\n          decoded += '\\n';\n          break;\n        case 'r':\n          decoded += '\\r';\n          break;\n        case 't':\n          decoded += '\\t';\n          break;\n        case 'u': {\n          unsigned int unicode;\n          if (!decodeUnicodeCodePoint(token, current, end, unicode)) return false;\n          decoded += codePointToUTF8(unicode);\n        } break;\n        default:\n          return addError(\"Bad escape sequence in string\", token, current);\n      }\n    } else {\n      decoded += c;\n    }\n  }\n  return true;\n}\n\nbool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode) {\n  if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) return false;\n  if (unicode >= 0xD800 && unicode <= 0xDBFF) {\n    // surrogate pairs\n    if (end - current < 6) return addError(\"additional six characters expected to parse unicode surrogate pair.\", token, current);\n    unsigned int surrogatePair;\n    if (*(current++) == '\\\\' && *(current++) == 'u') {\n      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {\n        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);\n      } else\n        return false;\n    } else\n      return addError(\n          \"expecting another \\\\u token to begin the second half of \"\n          \"a unicode surrogate pair\",\n          token, current);\n  }\n  return true;\n}\n\nbool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, unsigned int& ret_unicode) {\n  if (end - current < 4) return addError(\"Bad unicode escape sequence in string: four digits expected.\", token, current);\n  int unicode = 0;\n  for (int index = 0; index < 4; ++index) {\n    Char c = *current++;\n    unicode *= 16;\n    if (c >= '0' && c <= '9')\n      unicode += c - '0';\n    else if (c >= 'a' && c <= 'f')\n      unicode += c - 'a' + 10;\n    else if (c >= 'A' && c <= 'F')\n      unicode += c - 'A' + 10;\n    else\n      return addError(\"Bad unicode escape sequence in string: hexadecimal digit expected.\", token, current);\n  }\n  ret_unicode = static_cast<unsigned int>(unicode);\n  return true;\n}\n\nbool OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) {\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = extra;\n  errors_.push_back(info);\n  return false;\n}\n\nbool OurReader::recoverFromError(TokenType skipUntilToken) {\n  size_t errorCount = errors_.size();\n  Token skip;\n  for (;;) {\n    if (!readToken(skip)) errors_.resize(errorCount);  // discard errors caused by recovery\n    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) break;\n  }\n  errors_.resize(errorCount);\n  return false;\n}\n\nbool OurReader::addErrorAndRecover(const JSONCPP_STRING& message, Token& token, TokenType skipUntilToken) {\n  addError(message, token);\n  return recoverFromError(skipUntilToken);\n}\n\nValue& OurReader::currentValue() { return *(nodes_.top()); }\n\nOurReader::Char OurReader::getNextChar() {\n  if (current_ == end_) return 0;\n  return *current_++;\n}\n\nvoid OurReader::getLocationLineAndColumn(Location location, int& line, int& column) const {\n  Location current = begin_;\n  Location lastLineStart = current;\n  line = 0;\n  while (current < location && current != end_) {\n    Char c = *current++;\n    if (c == '\\r') {\n      if (*current == '\\n') ++current;\n      lastLineStart = current;\n      ++line;\n    } else if (c == '\\n') {\n      lastLineStart = current;\n      ++line;\n    }\n  }\n  // column & line start at 1\n  column = int(location - lastLineStart) + 1;\n  ++line;\n}\n\nJSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const {\n  int line, column;\n  getLocationLineAndColumn(location, line, column);\n  char buffer[18 + 16 + 16 + 1];\n  snprintf(buffer, sizeof(buffer), \"Line %d, Column %d\", line, column);\n  return buffer;\n}\n\nJSONCPP_STRING OurReader::getFormattedErrorMessages() const {\n  JSONCPP_STRING formattedMessage;\n  for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) {\n    const ErrorInfo& error = *itError;\n    formattedMessage += \"* \" + getLocationLineAndColumn(error.token_.start_) + \"\\n\";\n    formattedMessage += \"  \" + error.message_ + \"\\n\";\n    if (error.extra_) formattedMessage += \"See \" + getLocationLineAndColumn(error.extra_) + \" for detail.\\n\";\n  }\n  return formattedMessage;\n}\n\nstd::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {\n  std::vector<OurReader::StructuredError> allErrors;\n  for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) {\n    const ErrorInfo& error = *itError;\n    OurReader::StructuredError structured;\n    structured.offset_start = error.token_.start_ - begin_;\n    structured.offset_limit = error.token_.end_ - begin_;\n    structured.message = error.message_;\n    allErrors.push_back(structured);\n  }\n  return allErrors;\n}\n\nbool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) {\n  ptrdiff_t length = end_ - begin_;\n  if (value.getOffsetStart() > length || value.getOffsetLimit() > length) return false;\n  Token token;\n  token.type_ = tokenError;\n  token.start_ = begin_ + value.getOffsetStart();\n  token.end_ = end_ + value.getOffsetLimit();\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = 0;\n  errors_.push_back(info);\n  return true;\n}\n\nbool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) {\n  ptrdiff_t length = end_ - begin_;\n  if (value.getOffsetStart() > length || value.getOffsetLimit() > length || extra.getOffsetLimit() > length) return false;\n  Token token;\n  token.type_ = tokenError;\n  token.start_ = begin_ + value.getOffsetStart();\n  token.end_ = begin_ + value.getOffsetLimit();\n  ErrorInfo info;\n  info.token_ = token;\n  info.message_ = message;\n  info.extra_ = begin_ + extra.getOffsetStart();\n  errors_.push_back(info);\n  return true;\n}\n\nbool OurReader::good() const { return !errors_.size(); }\n\nclass OurCharReader : public CharReader {\n  bool const collectComments_;\n  OurReader reader_;\n\n public:\n  OurCharReader(bool collectComments, OurFeatures const& features) : collectComments_(collectComments), reader_(features) {}\n  bool parse(char const* beginDoc, char const* endDoc, Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE {\n    bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);\n    if (errs) {\n      *errs = reader_.getFormattedErrorMessages();\n    }\n    return ok;\n  }\n};\n\nCharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }\nCharReaderBuilder::~CharReaderBuilder() {}\nCharReader* CharReaderBuilder::newCharReader() const {\n  bool collectComments = settings_[\"collectComments\"].asBool();\n  OurFeatures features = OurFeatures::all();\n  features.allowComments_ = settings_[\"allowComments\"].asBool();\n  features.strictRoot_ = settings_[\"strictRoot\"].asBool();\n  features.allowDroppedNullPlaceholders_ = settings_[\"allowDroppedNullPlaceholders\"].asBool();\n  features.allowNumericKeys_ = settings_[\"allowNumericKeys\"].asBool();\n  features.allowSingleQuotes_ = settings_[\"allowSingleQuotes\"].asBool();\n  features.stackLimit_ = settings_[\"stackLimit\"].asInt();\n  features.failIfExtra_ = settings_[\"failIfExtra\"].asBool();\n  features.rejectDupKeys_ = settings_[\"rejectDupKeys\"].asBool();\n  features.allowSpecialFloats_ = settings_[\"allowSpecialFloats\"].asBool();\n  return new OurCharReader(collectComments, features);\n}\nstatic void getValidReaderKeys(std::set<JSONCPP_STRING>* valid_keys) {\n  valid_keys->clear();\n  valid_keys->insert(\"collectComments\");\n  valid_keys->insert(\"allowComments\");\n  valid_keys->insert(\"strictRoot\");\n  valid_keys->insert(\"allowDroppedNullPlaceholders\");\n  valid_keys->insert(\"allowNumericKeys\");\n  valid_keys->insert(\"allowSingleQuotes\");\n  valid_keys->insert(\"stackLimit\");\n  valid_keys->insert(\"failIfExtra\");\n  valid_keys->insert(\"rejectDupKeys\");\n  valid_keys->insert(\"allowSpecialFloats\");\n}\nbool CharReaderBuilder::validate(Json::Value* invalid) const {\n  Json::Value my_invalid;\n  if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL\n  Json::Value& inv = *invalid;\n  std::set<JSONCPP_STRING> valid_keys;\n  getValidReaderKeys(&valid_keys);\n  Value::Members keys = settings_.getMemberNames();\n  size_t n = keys.size();\n  for (size_t i = 0; i < n; ++i) {\n    JSONCPP_STRING const& key = keys[i];\n    if (valid_keys.find(key) == valid_keys.end()) {\n      inv[key] = settings_[key];\n    }\n  }\n  return 0u == inv.size();\n}\nValue& CharReaderBuilder::operator[](JSONCPP_STRING key) { return settings_[key]; }\n// static\nvoid CharReaderBuilder::strictMode(Json::Value* settings) {\n  //! [CharReaderBuilderStrictMode]\n  (*settings)[\"allowComments\"] = false;\n  (*settings)[\"strictRoot\"] = true;\n  (*settings)[\"allowDroppedNullPlaceholders\"] = false;\n  (*settings)[\"allowNumericKeys\"] = false;\n  (*settings)[\"allowSingleQuotes\"] = false;\n  (*settings)[\"stackLimit\"] = 1000;\n  (*settings)[\"failIfExtra\"] = true;\n  (*settings)[\"rejectDupKeys\"] = true;\n  (*settings)[\"allowSpecialFloats\"] = false;\n  //! [CharReaderBuilderStrictMode]\n}\n// static\nvoid CharReaderBuilder::setDefaults(Json::Value* settings) {\n  //! [CharReaderBuilderDefaults]\n  (*settings)[\"collectComments\"] = true;\n  (*settings)[\"allowComments\"] = true;\n  (*settings)[\"strictRoot\"] = false;\n  (*settings)[\"allowDroppedNullPlaceholders\"] = false;\n  (*settings)[\"allowNumericKeys\"] = false;\n  (*settings)[\"allowSingleQuotes\"] = false;\n  (*settings)[\"stackLimit\"] = 1000;\n  (*settings)[\"failIfExtra\"] = false;\n  (*settings)[\"rejectDupKeys\"] = false;\n  (*settings)[\"allowSpecialFloats\"] = false;\n  //! [CharReaderBuilderDefaults]\n}\n\n//////////////////////////////////\n// global functions\n\nbool parseFromStream(CharReader::Factory const& fact, JSONCPP_ISTREAM& sin, Value* root, JSONCPP_STRING* errs) {\n  JSONCPP_OSTRINGSTREAM ssin;\n  ssin << sin.rdbuf();\n  JSONCPP_STRING doc = ssin.str();\n  char const* begin = doc.data();\n  char const* end = begin + doc.size();\n  // Note that we do not actually need a null-terminator.\n  CharReaderPtr const reader(fact.newCharReader());\n  return reader->parse(begin, end, root, errs);\n}\n\nJSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) {\n  CharReaderBuilder b;\n  JSONCPP_STRING errs;\n  bool ok = parseFromStream(b, sin, &root, &errs);\n  if (!ok) {\n    fprintf(stderr, \"Error from reader: %s\", errs.c_str());\n\n    throwRuntimeError(errs);\n  }\n  return sin;\n}\n\n}  // namespace Json\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_reader.cpp\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_valueiterator.inl\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2007-2010 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n// included by json_value.cpp\n\nnamespace Json {\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class ValueIteratorBase\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\nValueIteratorBase::ValueIteratorBase() : current_(), isNull_(true) {}\n\nValueIteratorBase::ValueIteratorBase(const Value::ObjectValues::iterator& current) : current_(current), isNull_(false) {}\n\nValue& ValueIteratorBase::deref() const { return current_->second; }\n\nvoid ValueIteratorBase::increment() { ++current_; }\n\nvoid ValueIteratorBase::decrement() { --current_; }\n\nValueIteratorBase::difference_type ValueIteratorBase::computeDistance(const SelfType& other) const {\n#ifdef JSON_USE_CPPTL_SMALLMAP\n  return other.current_ - current_;\n#else\n  // Iterator for null value are initialized using the default\n  // constructor, which initialize current_ to the default\n  // std::map::iterator. As begin() and end() are two instance\n  // of the default std::map::iterator, they can not be compared.\n  // To allow this, we handle this comparison specifically.\n  if (isNull_ && other.isNull_) {\n    return 0;\n  }\n\n  // Usage of std::distance is not portable (does not compile with Sun Studio 12\n  // RogueWave STL,\n  // which is the one used by default).\n  // Using a portable hand-made version for non random iterator instead:\n  //   return difference_type( std::distance( current_, other.current_ ) );\n  difference_type myDistance = 0;\n  for (Value::ObjectValues::iterator it = current_; it != other.current_; ++it) {\n    ++myDistance;\n  }\n  return myDistance;\n#endif\n}\n\nbool ValueIteratorBase::isEqual(const SelfType& other) const {\n  if (isNull_) {\n    return other.isNull_;\n  }\n  return current_ == other.current_;\n}\n\nvoid ValueIteratorBase::copy(const SelfType& other) {\n  current_ = other.current_;\n  isNull_ = other.isNull_;\n}\n\nValue ValueIteratorBase::key() const {\n  const Value::CZString czstring = (*current_).first;\n  if (czstring.data()) {\n    if (czstring.isStaticString()) return Value(StaticString(czstring.data()));\n    return Value(czstring.data(), czstring.data() + czstring.length());\n  }\n  return Value(czstring.index());\n}\n\nUInt ValueIteratorBase::index() const {\n  const Value::CZString czstring = (*current_).first;\n  if (!czstring.data()) return czstring.index();\n  return Value::UInt(-1);\n}\n\nJSONCPP_STRING ValueIteratorBase::name() const {\n  char const* keey;\n  char const* end;\n  keey = memberName(&end);\n  if (!keey) return JSONCPP_STRING();\n  return JSONCPP_STRING(keey, end);\n}\n\nchar const* ValueIteratorBase::memberName() const {\n  const char* cname = (*current_).first.data();\n  return cname ? cname : \"\";\n}\n\nchar const* ValueIteratorBase::memberName(char const** end) const {\n  const char* cname = (*current_).first.data();\n  if (!cname) {\n    *end = NULL;\n    return NULL;\n  }\n  *end = cname + (*current_).first.length();\n  return cname;\n}\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class ValueConstIterator\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\nValueConstIterator::ValueConstIterator() {}\n\nValueConstIterator::ValueConstIterator(const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {}\n\nValueConstIterator::ValueConstIterator(ValueIterator const& other) : ValueIteratorBase(other) {}\n\nValueConstIterator& ValueConstIterator::operator=(const ValueIteratorBase& other) {\n  copy(other);\n  return *this;\n}\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class ValueIterator\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\nValueIterator::ValueIterator() {}\n\nValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) : ValueIteratorBase(current) {}\n\nValueIterator::ValueIterator(const ValueConstIterator& other) : ValueIteratorBase(other) {\n  throwRuntimeError(\"ConstIterator to Iterator should never be allowed.\");\n}\n\nValueIterator::ValueIterator(const ValueIterator& other) : ValueIteratorBase(other) {}\n\nValueIterator& ValueIterator::operator=(const SelfType& other) {\n  copy(other);\n  return *this;\n}\n\n}  // namespace Json\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_valueiterator.inl\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_value.cpp\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2011 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include <json/assertions.h>\n#include <json/value.h>\n#include <json/writer.h>\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n#include <math.h>\n#include <cassert>\n#include <cstring>\n#include <sstream>\n#include <utility>\n#ifdef JSON_USE_CPPTL\n#include <cpptl/conststring.h>\n#endif\n#include <algorithm>  // min()\n#include <cstddef>    // size_t\n\n#define JSON_ASSERT_UNREACHABLE assert(false)\n\nnamespace Json {\n\n// This is a walkaround to avoid the static initialization of Value::null.\n// kNull must be word-aligned to avoid crashing on ARM.  We use an alignment of\n// 8 (instead of 4) as a bit of future-proofing.\n#if defined(__ARMEL__)\n#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))\n#else\n#define ALIGNAS(byte_alignment)\n#endif\n// static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };\n// const unsigned char& kNullRef = kNull[0];\n// const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);\n// const Value& Value::nullRef = null;\n\n// static\nValue const& Value::nullSingleton() {\n  static Value const nullStatic;\n  return nullStatic;\n}\n\n// for backwards compatibility, we'll leave these global references around, but DO NOT\n// use them in JSONCPP library code any more!\nValue const& Value::null = Value::nullSingleton();\nValue const& Value::nullRef = Value::nullSingleton();\n\nconst Int Value::minInt = Int(~(UInt(-1) / 2));\nconst Int Value::maxInt = Int(UInt(-1) / 2);\nconst UInt Value::maxUInt = UInt(-1);\n#if defined(JSON_HAS_INT64)\nconst Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));\nconst Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);\nconst UInt64 Value::maxUInt64 = UInt64(-1);\n// The constant is hard-coded because some compiler have trouble\n// converting Value::maxUInt64 to a double correctly (AIX/xlC).\n// Assumes that UInt64 is a 64 bits integer.\nstatic const double maxUInt64AsDouble = 18446744073709551615.0;\n#endif  // defined(JSON_HAS_INT64)\nconst LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));\nconst LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);\nconst LargestUInt Value::maxLargestUInt = LargestUInt(-1);\n\n#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\ntemplate <typename T, typename U>\nstatic inline bool InRange(double d, T min, U max) {\n  // The casts can lose precision, but we are looking only for\n  // an approximate range. Might fail on edge cases though. ~cdunn\n  // return d >= static_cast<double>(min) && d <= static_cast<double>(max);\n  return d >= min && d <= max;\n}\n#else   // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\nstatic inline double integerToDouble(Json::UInt64 value) {\n  return static_cast<double>(Int64(value / 2)) * 2.0 + static_cast<double>(Int64(value & 1));\n}\n\ntemplate <typename T>\nstatic inline double integerToDouble(T value) {\n  return static_cast<double>(value);\n}\n\ntemplate <typename T, typename U>\nstatic inline bool InRange(double d, T min, U max) {\n  return d >= integerToDouble(min) && d <= integerToDouble(max);\n}\n#endif  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n\n/** Duplicates the specified string value.\n * @param value Pointer to the string to duplicate. Must be zero-terminated if\n *              length is \"unknown\".\n * @param length Length of the value. if equals to unknown, then it will be\n *               computed using strlen(value).\n * @return Pointer on the duplicate instance of string.\n */\nstatic inline char* duplicateStringValue(const char* value, size_t length) {\n  // Avoid an integer overflow in the call to malloc below by limiting length\n  // to a sane value.\n  if (length >= static_cast<size_t>(Value::maxInt)) length = Value::maxInt - 1;\n\n  char* newString = static_cast<char*>(malloc(length + 1));\n  if (newString == NULL) {\n    throwRuntimeError(\n        \"in Json::Value::duplicateStringValue(): \"\n        \"Failed to allocate string value buffer\");\n  }\n  memcpy(newString, value, length);\n  newString[length] = 0;\n  return newString;\n}\n\n/* Record the length as a prefix.\n */\nstatic inline char* duplicateAndPrefixStringValue(const char* value, unsigned int length) {\n  // Avoid an integer overflow in the call to malloc below by limiting length\n  // to a sane value.\n  JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - sizeof(unsigned) - 1U,\n                      \"in Json::Value::duplicateAndPrefixStringValue(): \"\n                      \"length too big for prefixing\");\n  unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U;\n  char* newString = static_cast<char*>(malloc(actualLength));\n  if (newString == 0) {\n    throwRuntimeError(\n        \"in Json::Value::duplicateAndPrefixStringValue(): \"\n        \"Failed to allocate string value buffer\");\n  }\n  *reinterpret_cast<unsigned*>(newString) = length;\n  memcpy(newString + sizeof(unsigned), value, length);\n  newString[actualLength - 1U] = 0;  // to avoid buffer over-run accidents by users later\n  return newString;\n}\ninline static void decodePrefixedString(bool isPrefixed, char const* prefixed, unsigned* length, char const** value) {\n  if (!isPrefixed) {\n    *length = static_cast<unsigned>(strlen(prefixed));\n    *value = prefixed;\n  } else {\n    *length = *reinterpret_cast<unsigned const*>(prefixed);\n    *value = prefixed + sizeof(unsigned);\n  }\n}\n/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue().\n */\n#if JSONCPP_USING_SECURE_MEMORY\nstatic inline void releasePrefixedStringValue(char* value) {\n  unsigned length = 0;\n  char const* valueDecoded;\n  decodePrefixedString(true, value, &length, &valueDecoded);\n  size_t const size = sizeof(unsigned) + length + 1U;\n  memset(value, 0, size);\n  free(value);\n}\nstatic inline void releaseStringValue(char* value, unsigned length) {\n  // length==0 => we allocated the strings memory\n  size_t size = (length == 0) ? strlen(value) : length;\n  memset(value, 0, size);\n  free(value);\n}\n#else   // !JSONCPP_USING_SECURE_MEMORY\nstatic inline void releasePrefixedStringValue(char* value) { free(value); }\nstatic inline void releaseStringValue(char* value, unsigned) { free(value); }\n#endif  // JSONCPP_USING_SECURE_MEMORY\n\n}  // namespace Json\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// ValueInternals...\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n#if !defined(JSON_IS_AMALGAMATION)\n\n#include \"json_valueiterator.inl\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n\nnamespace Json {\n\nException::Exception(JSONCPP_STRING const& msg) : msg_(msg) {}\nException::~Exception() throw() {}\nchar const* Exception::what() const throw() { return msg_.c_str(); }\nRuntimeError::RuntimeError(JSONCPP_STRING const& msg) : Exception(msg) {}\nLogicError::LogicError(JSONCPP_STRING const& msg) : Exception(msg) {}\nJSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg) { throw RuntimeError(msg); }\nJSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg) { throw LogicError(msg); }\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class Value::CommentInfo\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\nValue::CommentInfo::CommentInfo() : comment_(0) {}\n\nValue::CommentInfo::~CommentInfo() {\n  if (comment_) releaseStringValue(comment_, 0u);\n}\n\nvoid Value::CommentInfo::setComment(const char* text, size_t len) {\n  if (comment_) {\n    releaseStringValue(comment_, 0u);\n    comment_ = 0;\n  }\n  JSON_ASSERT(text != 0);\n  JSON_ASSERT_MESSAGE(text[0] == '\\0' || text[0] == '/', \"in Json::Value::setComment(): Comments must start with /\");\n  // It seems that /**/ style comments are acceptable as well.\n  comment_ = duplicateStringValue(text, len);\n}\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class Value::CZString\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\n// Notes: policy_ indicates if the string was allocated when\n// a string is stored.\n\nValue::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {}\n\nValue::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) : cstr_(str) {\n  // allocate != duplicate\n  storage_.policy_ = allocate & 0x3;\n  storage_.length_ = ulength & 0x3FFFFFFF;\n}\n\nValue::CZString::CZString(const CZString& other) {\n  cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0 ? duplicateStringValue(other.cstr_, other.storage_.length_) : other.cstr_);\n  storage_.policy_ =\n      static_cast<unsigned>(other.cstr_ ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == noDuplication ? noDuplication : duplicate)\n                                        : static_cast<DuplicationPolicy>(other.storage_.policy_)) &\n      3U;\n  storage_.length_ = other.storage_.length_;\n}\n\n#if JSON_HAS_RVALUE_REFERENCES\nValue::CZString::CZString(CZString&& other) : cstr_(other.cstr_), index_(other.index_) { other.cstr_ = nullptr; }\n#endif\n\nValue::CZString::~CZString() {\n  if (cstr_ && storage_.policy_ == duplicate) {\n    releaseStringValue(const_cast<char*>(cstr_),\n                       storage_.length_ + 1u);  //+1 for null terminating character for sake of completeness but not actually necessary\n  }\n}\n\nvoid Value::CZString::swap(CZString& other) {\n  std::swap(cstr_, other.cstr_);\n  std::swap(index_, other.index_);\n}\n\nValue::CZString& Value::CZString::operator=(CZString other) {\n  swap(other);\n  return *this;\n}\n\nbool Value::CZString::operator<(const CZString& other) const {\n  if (!cstr_) return index_ < other.index_;\n  // return strcmp(cstr_, other.cstr_) < 0;\n  // Assume both are strings.\n  unsigned this_len = this->storage_.length_;\n  unsigned other_len = other.storage_.length_;\n  unsigned min_len = std::min(this_len, other_len);\n  JSON_ASSERT(this->cstr_ && other.cstr_);\n  int comp = memcmp(this->cstr_, other.cstr_, min_len);\n  if (comp < 0) return true;\n  if (comp > 0) return false;\n  return (this_len < other_len);\n}\n\nbool Value::CZString::operator==(const CZString& other) const {\n  if (!cstr_) return index_ == other.index_;\n  // return strcmp(cstr_, other.cstr_) == 0;\n  // Assume both are strings.\n  unsigned this_len = this->storage_.length_;\n  unsigned other_len = other.storage_.length_;\n  if (this_len != other_len) return false;\n  JSON_ASSERT(this->cstr_ && other.cstr_);\n  int comp = memcmp(this->cstr_, other.cstr_, this_len);\n  return comp == 0;\n}\n\nArrayIndex Value::CZString::index() const { return index_; }\n\n// const char* Value::CZString::c_str() const { return cstr_; }\nconst char* Value::CZString::data() const { return cstr_; }\nunsigned Value::CZString::length() const { return storage_.length_; }\nbool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }\n\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// class Value::Value\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n// //////////////////////////////////////////////////////////////////\n\n/*! \\internal Default constructor initialization must be equivalent to:\n * memset( this, 0, sizeof(Value) )\n * This optimization is used in ValueInternalMap fast allocator.\n */\nValue::Value(ValueType vtype) {\n  initBasic(vtype);\n  switch (vtype) {\n    case nullValue:\n      break;\n    case intValue:\n    case uintValue:\n      value_.int_ = 0;\n      break;\n    case realValue:\n      value_.real_ = 0.0;\n      break;\n    case stringValue:\n      value_.string_ = 0;\n      break;\n    case arrayValue:\n    case objectValue:\n      value_.map_ = new ObjectValues();\n      break;\n    case booleanValue:\n      value_.bool_ = false;\n      break;\n    default:\n      JSON_ASSERT_UNREACHABLE;\n  }\n}\n\nValue::Value(Int value) {\n  initBasic(intValue);\n  value_.int_ = value;\n}\n\nValue::Value(UInt value) {\n  initBasic(uintValue);\n  value_.uint_ = value;\n}\n#if defined(JSON_HAS_INT64)\nValue::Value(Int64 value) {\n  initBasic(intValue);\n  value_.int_ = value;\n}\nValue::Value(UInt64 value) {\n  initBasic(uintValue);\n  value_.uint_ = value;\n}\n#endif  // defined(JSON_HAS_INT64)\n\nValue::Value(double value) {\n  initBasic(realValue);\n  value_.real_ = value;\n}\n\nValue::Value(const char* value) {\n  initBasic(stringValue, true);\n  value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));\n}\n\nValue::Value(const char* beginValue, const char* endValue) {\n  initBasic(stringValue, true);\n  value_.string_ = duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue));\n}\n\nValue::Value(const JSONCPP_STRING& value) {\n  initBasic(stringValue, true);\n  value_.string_ = duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length()));\n}\n\nValue::Value(const StaticString& value) {\n  initBasic(stringValue);\n  value_.string_ = const_cast<char*>(value.c_str());\n}\n\n#ifdef JSON_USE_CPPTL\nValue::Value(const CppTL::ConstString& value) {\n  initBasic(stringValue, true);\n  value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length()));\n}\n#endif\n\nValue::Value(bool value) {\n  initBasic(booleanValue);\n  value_.bool_ = value;\n}\n\nValue::Value(Value const& other) : type_(other.type_), allocated_(false), comments_(0), start_(other.start_), limit_(other.limit_) {\n  switch (type_) {\n    case nullValue:\n    case intValue:\n    case uintValue:\n    case realValue:\n    case booleanValue:\n      value_ = other.value_;\n      break;\n    case stringValue:\n      if (other.value_.string_ && other.allocated_) {\n        unsigned len;\n        char const* str;\n        decodePrefixedString(other.allocated_, other.value_.string_, &len, &str);\n        value_.string_ = duplicateAndPrefixStringValue(str, len);\n        allocated_ = true;\n      } else {\n        value_.string_ = other.value_.string_;\n        allocated_ = false;\n      }\n      break;\n    case arrayValue:\n    case objectValue:\n      value_.map_ = new ObjectValues(*other.value_.map_);\n      break;\n    default:\n      JSON_ASSERT_UNREACHABLE;\n  }\n  if (other.comments_) {\n    comments_ = new CommentInfo[numberOfCommentPlacement];\n    for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {\n      const CommentInfo& otherComment = other.comments_[comment];\n      if (otherComment.comment_) comments_[comment].setComment(otherComment.comment_, strlen(otherComment.comment_));\n    }\n  }\n}\n\n#if JSON_HAS_RVALUE_REFERENCES\n// Move constructor\nValue::Value(Value&& other) {\n  initBasic(nullValue);\n  swap(other);\n}\n#endif\n\nValue::~Value() {\n  switch (type_) {\n    case nullValue:\n    case intValue:\n    case uintValue:\n    case realValue:\n    case booleanValue:\n      break;\n    case stringValue:\n      if (allocated_) releasePrefixedStringValue(value_.string_);\n      break;\n    case arrayValue:\n    case objectValue:\n      delete value_.map_;\n      break;\n    default:\n      JSON_ASSERT_UNREACHABLE;\n  }\n\n  if (comments_) delete[] comments_;\n\n  value_.uint_ = 0;\n}\n\nValue& Value::operator=(Value other) {\n  swap(other);\n  return *this;\n}\n\nvoid Value::swapPayload(Value& other) {\n  ValueType temp = type_;\n  type_ = other.type_;\n  other.type_ = temp;\n  std::swap(value_, other.value_);\n  int temp2 = allocated_;\n  allocated_ = other.allocated_;\n  other.allocated_ = temp2 & 0x1;\n}\n\nvoid Value::swap(Value& other) {\n  swapPayload(other);\n  std::swap(comments_, other.comments_);\n  std::swap(start_, other.start_);\n  std::swap(limit_, other.limit_);\n}\n\nValueType Value::type() const { return type_; }\n\nint Value::compare(const Value& other) const {\n  if (*this < other) return -1;\n  if (*this > other) return 1;\n  return 0;\n}\n\nbool Value::operator<(const Value& other) const {\n  int typeDelta = type_ - other.type_;\n  if (typeDelta) return typeDelta < 0 ? true : false;\n  switch (type_) {\n    case nullValue:\n      return false;\n    case intValue:\n      return value_.int_ < other.value_.int_;\n    case uintValue:\n      return value_.uint_ < other.value_.uint_;\n    case realValue:\n      return value_.real_ < other.value_.real_;\n    case booleanValue:\n      return value_.bool_ < other.value_.bool_;\n    case stringValue: {\n      if ((value_.string_ == 0) || (other.value_.string_ == 0)) {\n        if (other.value_.string_)\n          return true;\n        else\n          return false;\n      }\n      unsigned this_len;\n      unsigned other_len;\n      char const* this_str;\n      char const* other_str;\n      decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n      decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);\n      unsigned min_len = std::min(this_len, other_len);\n      JSON_ASSERT(this_str && other_str);\n      int comp = memcmp(this_str, other_str, min_len);\n      if (comp < 0) return true;\n      if (comp > 0) return false;\n      return (this_len < other_len);\n    }\n    case arrayValue:\n    case objectValue: {\n      int delta = int(value_.map_->size() - other.value_.map_->size());\n      if (delta) return delta < 0;\n      return (*value_.map_) < (*other.value_.map_);\n    }\n    default:\n      JSON_ASSERT_UNREACHABLE;\n  }\n  return false;  // unreachable\n}\n\nbool Value::operator<=(const Value& other) const { return !(other < *this); }\n\nbool Value::operator>=(const Value& other) const { return !(*this < other); }\n\nbool Value::operator>(const Value& other) const { return other < *this; }\n\nbool Value::operator==(const Value& other) const {\n  // if ( type_ != other.type_ )\n  // GCC 2.95.3 says:\n  // attempt to take address of bit-field structure member `Json::Value::type_'\n  // Beats me, but a temp solves the problem.\n  int temp = other.type_;\n  if (type_ != temp) return false;\n  switch (type_) {\n    case nullValue:\n      return true;\n    case intValue:\n      return value_.int_ == other.value_.int_;\n    case uintValue:\n      return value_.uint_ == other.value_.uint_;\n    case realValue:\n      return value_.real_ == other.value_.real_;\n    case booleanValue:\n      return value_.bool_ == other.value_.bool_;\n    case stringValue: {\n      if ((value_.string_ == 0) || (other.value_.string_ == 0)) {\n        return (value_.string_ == other.value_.string_);\n      }\n      unsigned this_len;\n      unsigned other_len;\n      char const* this_str;\n      char const* other_str;\n      decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n      decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);\n      if (this_len != other_len) return false;\n      JSON_ASSERT(this_str && other_str);\n      int comp = memcmp(this_str, other_str, this_len);\n      return comp == 0;\n    }\n    case arrayValue:\n    case objectValue:\n      return value_.map_->size() == other.value_.map_->size() && (*value_.map_) == (*other.value_.map_);\n    default:\n      JSON_ASSERT_UNREACHABLE;\n  }\n  return false;  // unreachable\n}\n\nbool Value::operator!=(const Value& other) const { return !(*this == other); }\n\nconst char* Value::asCString() const {\n  JSON_ASSERT_MESSAGE(type_ == stringValue, \"in Json::Value::asCString(): requires stringValue\");\n  if (value_.string_ == 0) return 0;\n  unsigned this_len;\n  char const* this_str;\n  decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n  return this_str;\n}\n\n#if JSONCPP_USING_SECURE_MEMORY\nunsigned Value::getCStringLength() const {\n  JSON_ASSERT_MESSAGE(type_ == stringValue, \"in Json::Value::asCString(): requires stringValue\");\n  if (value_.string_ == 0) return 0;\n  unsigned this_len;\n  char const* this_str;\n  decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n  return this_len;\n}\n#endif\n\nbool Value::getString(char const** str, char const** cend) const {\n  if (type_ != stringValue) return false;\n  if (value_.string_ == 0) return false;\n  unsigned length;\n  decodePrefixedString(this->allocated_, this->value_.string_, &length, str);\n  *cend = *str + length;\n  return true;\n}\n\nJSONCPP_STRING Value::asString() const {\n  switch (type_) {\n    case nullValue:\n      return \"\";\n    case stringValue: {\n      if (value_.string_ == 0) return \"\";\n      unsigned this_len;\n      char const* this_str;\n      decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);\n      return JSONCPP_STRING(this_str, this_len);\n    }\n    case booleanValue:\n      return value_.bool_ ? \"true\" : \"false\";\n    case intValue:\n      return valueToString(value_.int_);\n    case uintValue:\n      return valueToString(value_.uint_);\n    case realValue:\n      return valueToString(value_.real_);\n    default:\n      JSON_FAIL_MESSAGE(\"Type is not convertible to string\");\n  }\n}\n\n#ifdef JSON_USE_CPPTL\nCppTL::ConstString Value::asConstString() const {\n  unsigned len;\n  char const* str;\n  decodePrefixedString(allocated_, value_.string_, &len, &str);\n  return CppTL::ConstString(str, len);\n}\n#endif\n\nValue::Int Value::asInt() const {\n  switch (type_) {\n    case intValue:\n      JSON_ASSERT_MESSAGE(isInt(), \"LargestInt out of Int range\");\n      return Int(value_.int_);\n    case uintValue:\n      JSON_ASSERT_MESSAGE(isInt(), \"LargestUInt out of Int range\");\n      return Int(value_.uint_);\n    case realValue:\n      JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), \"double out of Int range\");\n      return Int(value_.real_);\n    case nullValue:\n      return 0;\n    case booleanValue:\n      return value_.bool_ ? 1 : 0;\n    default:\n      break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to Int.\");\n}\n\nValue::UInt Value::asUInt() const {\n  switch (type_) {\n    case intValue:\n      JSON_ASSERT_MESSAGE(isUInt(), \"LargestInt out of UInt range\");\n      return UInt(value_.int_);\n    case uintValue:\n      JSON_ASSERT_MESSAGE(isUInt(), \"LargestUInt out of UInt range\");\n      return UInt(value_.uint_);\n    case realValue:\n      JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), \"double out of UInt range\");\n      return UInt(value_.real_);\n    case nullValue:\n      return 0;\n    case booleanValue:\n      return value_.bool_ ? 1 : 0;\n    default:\n      break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to UInt.\");\n}\n\n#if defined(JSON_HAS_INT64)\n\nValue::Int64 Value::asInt64() const {\n  switch (type_) {\n    case intValue:\n      return Int64(value_.int_);\n    case uintValue:\n      JSON_ASSERT_MESSAGE(isInt64(), \"LargestUInt out of Int64 range\");\n      return Int64(value_.uint_);\n    case realValue:\n      JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), \"double out of Int64 range\");\n      return Int64(value_.real_);\n    case nullValue:\n      return 0;\n    case booleanValue:\n      return value_.bool_ ? 1 : 0;\n    default:\n      break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to Int64.\");\n}\n\nValue::UInt64 Value::asUInt64() const {\n  switch (type_) {\n    case intValue:\n      JSON_ASSERT_MESSAGE(isUInt64(), \"LargestInt out of UInt64 range\");\n      return UInt64(value_.int_);\n    case uintValue:\n      return UInt64(value_.uint_);\n    case realValue:\n      JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), \"double out of UInt64 range\");\n      return UInt64(value_.real_);\n    case nullValue:\n      return 0;\n    case booleanValue:\n      return value_.bool_ ? 1 : 0;\n    default:\n      break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to UInt64.\");\n}\n#endif  // if defined(JSON_HAS_INT64)\n\nLargestInt Value::asLargestInt() const {\n#if defined(JSON_NO_INT64)\n  return asInt();\n#else\n  return asInt64();\n#endif\n}\n\nLargestUInt Value::asLargestUInt() const {\n#if defined(JSON_NO_INT64)\n  return asUInt();\n#else\n  return asUInt64();\n#endif\n}\n\ndouble Value::asDouble() const {\n  switch (type_) {\n    case intValue:\n      return static_cast<double>(value_.int_);\n    case uintValue:\n#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n      return static_cast<double>(value_.uint_);\n#else   // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n      return integerToDouble(value_.uint_);\n#endif  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n    case realValue:\n      return value_.real_;\n    case nullValue:\n      return 0.0;\n    case booleanValue:\n      return value_.bool_ ? 1.0 : 0.0;\n    default:\n      break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to double.\");\n}\n\nfloat Value::asFloat() const {\n  switch (type_) {\n    case intValue:\n      return static_cast<float>(value_.int_);\n    case uintValue:\n#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n      return static_cast<float>(value_.uint_);\n#else   // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n      // This can fail (silently?) if the value is bigger than MAX_FLOAT.\n      return static_cast<float>(integerToDouble(value_.uint_));\n#endif  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)\n    case realValue:\n      return static_cast<float>(value_.real_);\n    case nullValue:\n      return 0.0;\n    case booleanValue:\n      return value_.bool_ ? 1.0f : 0.0f;\n    default:\n      break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to float.\");\n}\n\nbool Value::asBool() const {\n  switch (type_) {\n    case booleanValue:\n      return value_.bool_;\n    case nullValue:\n      return false;\n    case intValue:\n      return value_.int_ ? true : false;\n    case uintValue:\n      return value_.uint_ ? true : false;\n    case realValue:\n      // This is kind of strange. Not recommended.\n      return (value_.real_ != 0.0) ? true : false;\n    default:\n      break;\n  }\n  JSON_FAIL_MESSAGE(\"Value is not convertible to bool.\");\n}\n\nbool Value::isConvertibleTo(ValueType other) const {\n  switch (other) {\n    case nullValue:\n      return (isNumeric() && asDouble() == 0.0) || (type_ == booleanValue && value_.bool_ == false) || (type_ == stringValue && asString() == \"\") ||\n             (type_ == arrayValue && value_.map_->size() == 0) || (type_ == objectValue && value_.map_->size() == 0) || type_ == nullValue;\n    case intValue:\n      return isInt() || (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || type_ == booleanValue || type_ == nullValue;\n    case uintValue:\n      return isUInt() || (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || type_ == booleanValue || type_ == nullValue;\n    case realValue:\n      return isNumeric() || type_ == booleanValue || type_ == nullValue;\n    case booleanValue:\n      return isNumeric() || type_ == booleanValue || type_ == nullValue;\n    case stringValue:\n      return isNumeric() || type_ == booleanValue || type_ == stringValue || type_ == nullValue;\n    case arrayValue:\n      return type_ == arrayValue || type_ == nullValue;\n    case objectValue:\n      return type_ == objectValue || type_ == nullValue;\n  }\n  JSON_ASSERT_UNREACHABLE;\n  return false;\n}\n\n/// Number of values in array or object\nArrayIndex Value::size() const {\n  switch (type_) {\n    case nullValue:\n    case intValue:\n    case uintValue:\n    case realValue:\n    case booleanValue:\n    case stringValue:\n      return 0;\n    case arrayValue:  // size of the array is highest index + 1\n      if (!value_.map_->empty()) {\n        ObjectValues::const_iterator itLast = value_.map_->end();\n        --itLast;\n        return (*itLast).first.index() + 1;\n      }\n      return 0;\n    case objectValue:\n      return ArrayIndex(value_.map_->size());\n  }\n  JSON_ASSERT_UNREACHABLE;\n  return 0;  // unreachable;\n}\n\nbool Value::empty() const {\n  if (isNull() || isArray() || isObject())\n    return size() == 0u;\n  else\n    return false;\n}\n\nbool Value::operator!() const { return isNull(); }\n\nvoid Value::clear() {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || type_ == objectValue, \"in Json::Value::clear(): requires complex value\");\n  start_ = 0;\n  limit_ = 0;\n  switch (type_) {\n    case arrayValue:\n    case objectValue:\n      value_.map_->clear();\n      break;\n    default:\n      break;\n  }\n}\n\nvoid Value::resize(ArrayIndex newSize) {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, \"in Json::Value::resize(): requires arrayValue\");\n  if (type_ == nullValue) *this = Value(arrayValue);\n  ArrayIndex oldSize = size();\n  if (newSize == 0)\n    clear();\n  else if (newSize > oldSize)\n    (*this)[newSize - 1];\n  else {\n    for (ArrayIndex index = newSize; index < oldSize; ++index) {\n      value_.map_->erase(index);\n    }\n    JSON_ASSERT(size() == newSize);\n  }\n}\n\nValue& Value::operator[](ArrayIndex index) {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, \"in Json::Value::operator[](ArrayIndex): requires arrayValue\");\n  if (type_ == nullValue) *this = Value(arrayValue);\n  CZString key(index);\n  ObjectValues::iterator it = value_.map_->lower_bound(key);\n  if (it != value_.map_->end() && (*it).first == key) return (*it).second;\n\n  ObjectValues::value_type defaultValue(key, nullSingleton());\n  it = value_.map_->insert(it, defaultValue);\n  return (*it).second;\n}\n\nValue& Value::operator[](int index) {\n  JSON_ASSERT_MESSAGE(index >= 0, \"in Json::Value::operator[](int index): index cannot be negative\");\n  return (*this)[ArrayIndex(index)];\n}\n\nconst Value& Value::operator[](ArrayIndex index) const {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, \"in Json::Value::operator[](ArrayIndex)const: requires arrayValue\");\n  if (type_ == nullValue) return nullSingleton();\n  CZString key(index);\n  ObjectValues::const_iterator it = value_.map_->find(key);\n  if (it == value_.map_->end()) return nullSingleton();\n  return (*it).second;\n}\n\nconst Value& Value::operator[](int index) const {\n  JSON_ASSERT_MESSAGE(index >= 0, \"in Json::Value::operator[](int index) const: index cannot be negative\");\n  return (*this)[ArrayIndex(index)];\n}\n\nvoid Value::initBasic(ValueType vtype, bool allocated) {\n  type_ = vtype;\n  allocated_ = allocated;\n  comments_ = 0;\n  start_ = 0;\n  limit_ = 0;\n}\n\n// Access an object value by name, create a null member if it does not exist.\n// @pre Type of '*this' is object or null.\n// @param key is null-terminated.\nValue& Value::resolveReference(const char* key) {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, \"in Json::Value::resolveReference(): requires objectValue\");\n  if (type_ == nullValue) *this = Value(objectValue);\n  CZString actualKey(key, static_cast<unsigned>(strlen(key)), CZString::noDuplication);  // NOTE!\n  ObjectValues::iterator it = value_.map_->lower_bound(actualKey);\n  if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second;\n\n  ObjectValues::value_type defaultValue(actualKey, nullSingleton());\n  it = value_.map_->insert(it, defaultValue);\n  Value& value = (*it).second;\n  return value;\n}\n\n// @param key is not null-terminated.\nValue& Value::resolveReference(char const* key, char const* cend) {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, \"in Json::Value::resolveReference(key, end): requires objectValue\");\n  if (type_ == nullValue) *this = Value(objectValue);\n  CZString actualKey(key, static_cast<unsigned>(cend - key), CZString::duplicateOnCopy);\n  ObjectValues::iterator it = value_.map_->lower_bound(actualKey);\n  if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second;\n\n  ObjectValues::value_type defaultValue(actualKey, nullSingleton());\n  it = value_.map_->insert(it, defaultValue);\n  Value& value = (*it).second;\n  return value;\n}\n\nValue Value::get(ArrayIndex index, const Value& defaultValue) const {\n  const Value* value = &((*this)[index]);\n  return value == &nullSingleton() ? defaultValue : *value;\n}\n\nbool Value::isValidIndex(ArrayIndex index) const { return index < size(); }\n\nValue const* Value::find(char const* key, char const* cend) const {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, \"in Json::Value::find(key, end, found): requires objectValue or nullValue\");\n  if (type_ == nullValue) return NULL;\n  CZString actualKey(key, static_cast<unsigned>(cend - key), CZString::noDuplication);\n  ObjectValues::const_iterator it = value_.map_->find(actualKey);\n  if (it == value_.map_->end()) return NULL;\n  return &(*it).second;\n}\nconst Value& Value::operator[](const char* key) const {\n  Value const* found = find(key, key + strlen(key));\n  if (!found) return nullSingleton();\n  return *found;\n}\nValue const& Value::operator[](JSONCPP_STRING const& key) const {\n  Value const* found = find(key.data(), key.data() + key.length());\n  if (!found) return nullSingleton();\n  return *found;\n}\n\nValue& Value::operator[](const char* key) { return resolveReference(key, key + strlen(key)); }\n\nValue& Value::operator[](const JSONCPP_STRING& key) { return resolveReference(key.data(), key.data() + key.length()); }\n\nValue& Value::operator[](const StaticString& key) { return resolveReference(key.c_str()); }\n\n#ifdef JSON_USE_CPPTL\nValue& Value::operator[](const CppTL::ConstString& key) { return resolveReference(key.c_str(), key.end_c_str()); }\nValue const& Value::operator[](CppTL::ConstString const& key) const {\n  Value const* found = find(key.c_str(), key.end_c_str());\n  if (!found) return nullSingleton();\n  return *found;\n}\n#endif\n\nValue& Value::append(const Value& value) { return (*this)[size()] = value; }\n\nValue Value::get(char const* key, char const* cend, Value const& defaultValue) const {\n  Value const* found = find(key, cend);\n  return !found ? defaultValue : *found;\n}\nValue Value::get(char const* key, Value const& defaultValue) const { return get(key, key + strlen(key), defaultValue); }\nValue Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const { return get(key.data(), key.data() + key.length(), defaultValue); }\n\nbool Value::removeMember(const char* key, const char* cend, Value* removed) {\n  if (type_ != objectValue) {\n    return false;\n  }\n  CZString actualKey(key, static_cast<unsigned>(cend - key), CZString::noDuplication);\n  ObjectValues::iterator it = value_.map_->find(actualKey);\n  if (it == value_.map_->end()) return false;\n  *removed = it->second;\n  value_.map_->erase(it);\n  return true;\n}\nbool Value::removeMember(const char* key, Value* removed) { return removeMember(key, key + strlen(key), removed); }\nbool Value::removeMember(JSONCPP_STRING const& key, Value* removed) { return removeMember(key.data(), key.data() + key.length(), removed); }\nValue Value::removeMember(const char* key) {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, \"in Json::Value::removeMember(): requires objectValue\");\n  if (type_ == nullValue) return nullSingleton();\n\n  Value removed;  // null\n  removeMember(key, key + strlen(key), &removed);\n  return removed;  // still null if removeMember() did nothing\n}\nValue Value::removeMember(const JSONCPP_STRING& key) { return removeMember(key.c_str()); }\n\nbool Value::removeIndex(ArrayIndex index, Value* removed) {\n  if (type_ != arrayValue) {\n    return false;\n  }\n  CZString key(index);\n  ObjectValues::iterator it = value_.map_->find(key);\n  if (it == value_.map_->end()) {\n    return false;\n  }\n  *removed = it->second;\n  ArrayIndex oldSize = size();\n  // shift left all items left, into the place of the \"removed\"\n  for (ArrayIndex i = index; i < (oldSize - 1); ++i) {\n    CZString keey(i);\n    (*value_.map_)[keey] = (*this)[i + 1];\n  }\n  // erase the last one (\"leftover\")\n  CZString keyLast(oldSize - 1);\n  ObjectValues::iterator itLast = value_.map_->find(keyLast);\n  value_.map_->erase(itLast);\n  return true;\n}\n\n#ifdef JSON_USE_CPPTL\nValue Value::get(const CppTL::ConstString& key, const Value& defaultValue) const { return get(key.c_str(), key.end_c_str(), defaultValue); }\n#endif\n\nbool Value::isMember(char const* key, char const* cend) const {\n  Value const* value = find(key, cend);\n  return NULL != value;\n}\nbool Value::isMember(char const* key) const { return isMember(key, key + strlen(key)); }\nbool Value::isMember(JSONCPP_STRING const& key) const { return isMember(key.data(), key.data() + key.length()); }\n\n#ifdef JSON_USE_CPPTL\nbool Value::isMember(const CppTL::ConstString& key) const { return isMember(key.c_str(), key.end_c_str()); }\n#endif\n\nValue::Members Value::getMemberNames() const {\n  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, \"in Json::Value::getMemberNames(), value must be objectValue\");\n  if (type_ == nullValue) return Value::Members();\n  Members members;\n  members.reserve(value_.map_->size());\n  ObjectValues::const_iterator it = value_.map_->begin();\n  ObjectValues::const_iterator itEnd = value_.map_->end();\n  for (; it != itEnd; ++it) {\n    members.push_back(JSONCPP_STRING((*it).first.data(), (*it).first.length()));\n  }\n  return members;\n}\n//\n//# ifdef JSON_USE_CPPTL\n// EnumMemberNames\n// Value::enumMemberNames() const\n//{\n//   if ( type_ == objectValue )\n//   {\n//      return CppTL::Enum::any(  CppTL::Enum::transform(\n//         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),\n//         MemberNamesTransform() ) );\n//   }\n//   return EnumMemberNames();\n//}\n//\n//\n// EnumValues\n// Value::enumValues() const\n//{\n//   if ( type_ == objectValue  ||  type_ == arrayValue )\n//      return CppTL::Enum::anyValues( *(value_.map_),\n//                                     CppTL::Type<const Value &>() );\n//   return EnumValues();\n//}\n//\n//# endif\n\nstatic bool IsIntegral(double d) {\n  double integral_part;\n  return modf(d, &integral_part) == 0.0;\n}\n\nbool Value::isNull() const { return type_ == nullValue; }\n\nbool Value::isBool() const { return type_ == booleanValue; }\n\nbool Value::isInt() const {\n  switch (type_) {\n    case intValue:\n      return value_.int_ >= minInt && value_.int_ <= maxInt;\n    case uintValue:\n      return value_.uint_ <= UInt(maxInt);\n    case realValue:\n      return value_.real_ >= minInt && value_.real_ <= maxInt && IsIntegral(value_.real_);\n    default:\n      break;\n  }\n  return false;\n}\n\nbool Value::isUInt() const {\n  switch (type_) {\n    case intValue:\n      return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);\n    case uintValue:\n      return value_.uint_ <= maxUInt;\n    case realValue:\n      return value_.real_ >= 0 && value_.real_ <= maxUInt && IsIntegral(value_.real_);\n    default:\n      break;\n  }\n  return false;\n}\n\nbool Value::isInt64() const {\n#if defined(JSON_HAS_INT64)\n  switch (type_) {\n    case intValue:\n      return true;\n    case uintValue:\n      return value_.uint_ <= UInt64(maxInt64);\n    case realValue:\n      // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a\n      // double, so double(maxInt64) will be rounded up to 2^63. Therefore we\n      // require the value to be strictly less than the limit.\n      return value_.real_ >= double(minInt64) && value_.real_ < double(maxInt64) && IsIntegral(value_.real_);\n    default:\n      break;\n  }\n#endif  // JSON_HAS_INT64\n  return false;\n}\n\nbool Value::isUInt64() const {\n#if defined(JSON_HAS_INT64)\n  switch (type_) {\n    case intValue:\n      return value_.int_ >= 0;\n    case uintValue:\n      return true;\n    case realValue:\n      // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a\n      // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we\n      // require the value to be strictly less than the limit.\n      return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);\n    default:\n      break;\n  }\n#endif  // JSON_HAS_INT64\n  return false;\n}\n\nbool Value::isIntegral() const {\n#if defined(JSON_HAS_INT64)\n  return isInt64() || isUInt64();\n#else\n  return isInt() || isUInt();\n#endif\n}\n\nbool Value::isDouble() const { return type_ == realValue || isIntegral(); }\n\nbool Value::isNumeric() const { return isIntegral() || isDouble(); }\n\nbool Value::isString() const { return type_ == stringValue; }\n\nbool Value::isArray() const { return type_ == arrayValue; }\n\nbool Value::isObject() const { return type_ == objectValue; }\n\nvoid Value::setComment(const char* comment, size_t len, CommentPlacement placement) {\n  if (!comments_) comments_ = new CommentInfo[numberOfCommentPlacement];\n  if ((len > 0) && (comment[len - 1] == '\\n')) {\n    // Always discard trailing newline, to aid indentation.\n    len -= 1;\n  }\n  comments_[placement].setComment(comment, len);\n}\n\nvoid Value::setComment(const char* comment, CommentPlacement placement) { setComment(comment, strlen(comment), placement); }\n\nvoid Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) { setComment(comment.c_str(), comment.length(), placement); }\n\nbool Value::hasComment(CommentPlacement placement) const { return comments_ != 0 && comments_[placement].comment_ != 0; }\n\nJSONCPP_STRING Value::getComment(CommentPlacement placement) const {\n  if (hasComment(placement)) return comments_[placement].comment_;\n  return \"\";\n}\n\nvoid Value::setOffsetStart(ptrdiff_t start) { start_ = start; }\n\nvoid Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; }\n\nptrdiff_t Value::getOffsetStart() const { return start_; }\n\nptrdiff_t Value::getOffsetLimit() const { return limit_; }\n\nJSONCPP_STRING Value::toStyledString() const {\n  StyledWriter writer;\n  return writer.write(*this);\n}\n\nValue::const_iterator Value::begin() const {\n  switch (type_) {\n    case arrayValue:\n    case objectValue:\n      if (value_.map_) return const_iterator(value_.map_->begin());\n      break;\n    default:\n      break;\n  }\n  return const_iterator();\n}\n\nValue::const_iterator Value::end() const {\n  switch (type_) {\n    case arrayValue:\n    case objectValue:\n      if (value_.map_) return const_iterator(value_.map_->end());\n      break;\n    default:\n      break;\n  }\n  return const_iterator();\n}\n\nValue::iterator Value::begin() {\n  switch (type_) {\n    case arrayValue:\n    case objectValue:\n      if (value_.map_) return iterator(value_.map_->begin());\n      break;\n    default:\n      break;\n  }\n  return iterator();\n}\n\nValue::iterator Value::end() {\n  switch (type_) {\n    case arrayValue:\n    case objectValue:\n      if (value_.map_) return iterator(value_.map_->end());\n      break;\n    default:\n      break;\n  }\n  return iterator();\n}\n\n// class PathArgument\n// //////////////////////////////////////////////////////////////////\n\nPathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {}\n\nPathArgument::PathArgument(ArrayIndex index) : key_(), index_(index), kind_(kindIndex) {}\n\nPathArgument::PathArgument(const char* key) : key_(key), index_(), kind_(kindKey) {}\n\nPathArgument::PathArgument(const JSONCPP_STRING& key) : key_(key.c_str()), index_(), kind_(kindKey) {}\n\n// class Path\n// //////////////////////////////////////////////////////////////////\n\nPath::Path(const JSONCPP_STRING& path, const PathArgument& a1, const PathArgument& a2, const PathArgument& a3, const PathArgument& a4,\n           const PathArgument& a5) {\n  InArgs in;\n  in.push_back(&a1);\n  in.push_back(&a2);\n  in.push_back(&a3);\n  in.push_back(&a4);\n  in.push_back(&a5);\n  makePath(path, in);\n}\n\nvoid Path::makePath(const JSONCPP_STRING& path, const InArgs& in) {\n  const char* current = path.c_str();\n  const char* end = current + path.length();\n  InArgs::const_iterator itInArg = in.begin();\n  while (current != end) {\n    if (*current == '[') {\n      ++current;\n      if (*current == '%')\n        addPathInArg(path, in, itInArg, PathArgument::kindIndex);\n      else {\n        ArrayIndex index = 0;\n        for (; current != end && *current >= '0' && *current <= '9'; ++current) index = index * 10 + ArrayIndex(*current - '0');\n        args_.push_back(index);\n      }\n      if (current == end || *current++ != ']') invalidPath(path, int(current - path.c_str()));\n    } else if (*current == '%') {\n      addPathInArg(path, in, itInArg, PathArgument::kindKey);\n      ++current;\n    } else if (*current == '.') {\n      ++current;\n    } else {\n      const char* beginName = current;\n      while (current != end && !strchr(\"[.\", *current)) ++current;\n      args_.push_back(JSONCPP_STRING(beginName, current));\n    }\n  }\n}\n\nvoid Path::addPathInArg(const JSONCPP_STRING& /*path*/, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind) {\n  if (itInArg == in.end()) {\n    // Error: missing argument %d\n  } else if ((*itInArg)->kind_ != kind) {\n    // Error: bad argument type\n  } else {\n    args_.push_back(**itInArg);\n  }\n}\n\nvoid Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) {\n  // Error: invalid path.\n}\n\nconst Value& Path::resolve(const Value& root) const {\n  const Value* node = &root;\n  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {\n    const PathArgument& arg = *it;\n    if (arg.kind_ == PathArgument::kindIndex) {\n      if (!node->isArray() || !node->isValidIndex(arg.index_)) {\n        // Error: unable to resolve path (array value expected at position...\n      }\n      node = &((*node)[arg.index_]);\n    } else if (arg.kind_ == PathArgument::kindKey) {\n      if (!node->isObject()) {\n        // Error: unable to resolve path (object value expected at position...)\n      }\n      node = &((*node)[arg.key_]);\n      if (node == &Value::nullSingleton()) {\n        // Error: unable to resolve path (object has no member named '' at\n        // position...)\n      }\n    }\n  }\n  return *node;\n}\n\nValue Path::resolve(const Value& root, const Value& defaultValue) const {\n  const Value* node = &root;\n  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {\n    const PathArgument& arg = *it;\n    if (arg.kind_ == PathArgument::kindIndex) {\n      if (!node->isArray() || !node->isValidIndex(arg.index_)) return defaultValue;\n      node = &((*node)[arg.index_]);\n    } else if (arg.kind_ == PathArgument::kindKey) {\n      if (!node->isObject()) return defaultValue;\n      node = &((*node)[arg.key_]);\n      if (node == &Value::nullSingleton()) return defaultValue;\n    }\n  }\n  return *node;\n}\n\nValue& Path::make(Value& root) const {\n  Value* node = &root;\n  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {\n    const PathArgument& arg = *it;\n    if (arg.kind_ == PathArgument::kindIndex) {\n      if (!node->isArray()) {\n        // Error: node is not an array at position ...\n      }\n      node = &((*node)[arg.index_]);\n    } else if (arg.kind_ == PathArgument::kindKey) {\n      if (!node->isObject()) {\n        // Error: node is not an object at position...\n      }\n      node = &((*node)[arg.key_]);\n    }\n  }\n  return *node;\n}\n\n}  // namespace Json\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_value.cpp\n// //////////////////////////////////////////////////////////////////////\n\n// //////////////////////////////////////////////////////////////////////\n// Beginning of content of file: src/lib_json/json_writer.cpp\n// //////////////////////////////////////////////////////////////////////\n\n// Copyright 2011 Baptiste Lepilleur\n// Distributed under MIT license, or public domain if desired and\n// recognized in your jurisdiction.\n// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE\n\n#if !defined(JSON_IS_AMALGAMATION)\n#include <json/writer.h>\n#include \"json_tool.h\"\n#endif  // if !defined(JSON_IS_AMALGAMATION)\n#include <cassert>\n#include <cstdio>\n#include <cstring>\n#include <iomanip>\n#include <memory>\n#include <set>\n#include <sstream>\n#include <utility>\n\n#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800  // Between VC++ 6.0 and VC++ 11.0\n#include <float.h>\n#define isfinite _finite\n#elif defined(__sun) && defined(__SVR4)  // Solaris\n#if !defined(isfinite)\n#include <ieeefp.h>\n#define isfinite finite\n#endif\n#elif defined(_AIX)\n#if !defined(isfinite)\n#include <math.h>\n#define isfinite finite\n#endif\n#elif defined(__hpux)\n#if !defined(isfinite)\n#if defined(__ia64) && !defined(finite)\n#define isfinite(x) ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))\n#else\n#include <math.h>\n#define isfinite finite\n#endif\n#endif\n#else\n#include <cmath>\n#if !(defined(__QNXNTO__))  // QNX already defines isfinite\n#define isfinite std::isfinite\n#endif\n#endif\n\n#if defined(_MSC_VER)\n#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500  // VC++ 9.0 and above\n#define snprintf sprintf_s\n#elif _MSC_VER >= 1900  // VC++ 14.0 and above\n#define snprintf std::snprintf\n#else\n#define snprintf _snprintf\n#endif\n#elif defined(__ANDROID__) || defined(__QNXNTO__)\n#define snprintf snprintf\n#elif __cplusplus >= 201103L\n#if !defined(__MINGW32__) && !defined(__CYGWIN__)\n#define snprintf std::snprintf\n#endif\n#endif\n\n#if defined(__BORLANDC__)\n#include <float.h>\n#define isfinite _finite\n#define snprintf _snprintf\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400  // VC++ 8.0\n// Disable warning about strdup being deprecated.\n#pragma warning(disable : 4996)\n#endif\n\nnamespace Json {\n\n#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)\ntypedef std::unique_ptr<StreamWriter> StreamWriterPtr;\n#else\ntypedef std::auto_ptr<StreamWriter> StreamWriterPtr;\n#endif\n\nstatic bool containsControlCharacter(const char* str) {\n  while (*str) {\n    if (isControlCharacter(*(str++))) return true;\n  }\n  return false;\n}\n\nstatic bool containsControlCharacter0(const char* str, unsigned len) {\n  char const* end = str + len;\n  while (end != str) {\n    if (isControlCharacter(*str) || 0 == *str) return true;\n    ++str;\n  }\n  return false;\n}\n\nJSONCPP_STRING valueToString(LargestInt value) {\n  UIntToStringBuffer buffer;\n  char* current = buffer + sizeof(buffer);\n  if (value == Value::minLargestInt) {\n    uintToString(LargestUInt(Value::maxLargestInt) + 1, current);\n    *--current = '-';\n  } else if (value < 0) {\n    uintToString(LargestUInt(-value), current);\n    *--current = '-';\n  } else {\n    uintToString(LargestUInt(value), current);\n  }\n  assert(current >= buffer);\n  return current;\n}\n\nJSONCPP_STRING valueToString(LargestUInt value) {\n  UIntToStringBuffer buffer;\n  char* current = buffer + sizeof(buffer);\n  uintToString(value, current);\n  assert(current >= buffer);\n  return current;\n}\n\n#if defined(JSON_HAS_INT64)\n\nJSONCPP_STRING valueToString(Int value) { return valueToString(LargestInt(value)); }\n\nJSONCPP_STRING valueToString(UInt value) { return valueToString(LargestUInt(value)); }\n\n#endif  // # if defined(JSON_HAS_INT64)\n\nnamespace {\nJSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {\n  // Allocate a buffer that is more than large enough to store the 16 digits of\n  // precision requested below.\n  char buffer[32];\n  int len = -1;\n\n  char formatString[6];\n  sprintf(formatString, \"%%.%dg\", precision);\n\n  // Print into the buffer. We need not request the alternative representation\n  // that always has a decimal point because JSON doesn't distingish the\n  // concepts of reals and integers.\n  if (isfinite(value)) {\n    len = snprintf(buffer, sizeof(buffer), formatString, value);\n  } else {\n    // IEEE standard states that NaN values will not compare to themselves\n    if (value != value) {\n      len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? \"NaN\" : \"null\");\n    } else if (value < 0) {\n      len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? \"-Infinity\" : \"-1e+9999\");\n    } else {\n      len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? \"Infinity\" : \"1e+9999\");\n    }\n    // For those, we do not need to call fixNumLoc, but it is fast.\n  }\n  assert(len >= 0);\n  fixNumericLocale(buffer, buffer + len);\n  return buffer;\n}\n}\n\nJSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }\n\nJSONCPP_STRING valueToString(bool value) { return value ? \"true\" : \"false\"; }\n\nJSONCPP_STRING valueToQuotedString(const char* value) {\n  if (value == NULL) return \"\";\n  // Not sure how to handle unicode...\n  if (strpbrk(value, \"\\\"\\\\\\b\\f\\n\\r\\t\") == NULL && !containsControlCharacter(value)) return JSONCPP_STRING(\"\\\"\") + value + \"\\\"\";\n  // We have to walk value and escape any special characters.\n  // Appending to JSONCPP_STRING is not efficient, but this should be rare.\n  // (Note: forward slashes are *not* rare, but I am not escaping them.)\n  JSONCPP_STRING::size_type maxsize = strlen(value) * 2 + 3;  // allescaped+quotes+NULL\n  JSONCPP_STRING result;\n  result.reserve(maxsize);  // to avoid lots of mallocs\n  result += \"\\\"\";\n  for (const char* c = value; *c != 0; ++c) {\n    switch (*c) {\n      case '\\\"':\n        result += \"\\\\\\\"\";\n        break;\n      case '\\\\':\n        result += \"\\\\\\\\\";\n        break;\n      case '\\b':\n        result += \"\\\\b\";\n        break;\n      case '\\f':\n        result += \"\\\\f\";\n        break;\n      case '\\n':\n        result += \"\\\\n\";\n        break;\n      case '\\r':\n        result += \"\\\\r\";\n        break;\n      case '\\t':\n        result += \"\\\\t\";\n        break;\n      // case '/':\n      // Even though \\/ is considered a legal escape in JSON, a bare\n      // slash is also legal, so I see no reason to escape it.\n      // (I hope I am not misunderstanding something.\n      // blep notes: actually escaping \\/ may be useful in javascript to avoid </\n      // sequence.\n      // Should add a flag to allow this compatibility mode and prevent this\n      // sequence from occurring.\n      default:\n        if (isControlCharacter(*c)) {\n          JSONCPP_OSTRINGSTREAM oss;\n          oss << \"\\\\u\" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);\n          result += oss.str();\n        } else {\n          result += *c;\n        }\n        break;\n    }\n  }\n  result += \"\\\"\";\n  return result;\n}\n\n// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp\nstatic char const* strnpbrk(char const* s, char const* accept, size_t n) {\n  assert((s || !n) && accept);\n\n  char const* const end = s + n;\n  for (char const* cur = s; cur < end; ++cur) {\n    int const c = *cur;\n    for (char const* a = accept; *a; ++a) {\n      if (*a == c) {\n        return cur;\n      }\n    }\n  }\n  return NULL;\n}\nstatic JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) {\n  if (value == NULL) return \"\";\n  // Not sure how to handle unicode...\n  if (strnpbrk(value, \"\\\"\\\\\\b\\f\\n\\r\\t\", length) == NULL && !containsControlCharacter0(value, length)) return JSONCPP_STRING(\"\\\"\") + value + \"\\\"\";\n  // We have to walk value and escape any special characters.\n  // Appending to JSONCPP_STRING is not efficient, but this should be rare.\n  // (Note: forward slashes are *not* rare, but I am not escaping them.)\n  JSONCPP_STRING::size_type maxsize = length * 2 + 3;  // allescaped+quotes+NULL\n  JSONCPP_STRING result;\n  result.reserve(maxsize);  // to avoid lots of mallocs\n  result += \"\\\"\";\n  char const* end = value + length;\n  for (const char* c = value; c != end; ++c) {\n    switch (*c) {\n      case '\\\"':\n        result += \"\\\\\\\"\";\n        break;\n      case '\\\\':\n        result += \"\\\\\\\\\";\n        break;\n      case '\\b':\n        result += \"\\\\b\";\n        break;\n      case '\\f':\n        result += \"\\\\f\";\n        break;\n      case '\\n':\n        result += \"\\\\n\";\n        break;\n      case '\\r':\n        result += \"\\\\r\";\n        break;\n      case '\\t':\n        result += \"\\\\t\";\n        break;\n      // case '/':\n      // Even though \\/ is considered a legal escape in JSON, a bare\n      // slash is also legal, so I see no reason to escape it.\n      // (I hope I am not misunderstanding something.)\n      // blep notes: actually escaping \\/ may be useful in javascript to avoid </\n      // sequence.\n      // Should add a flag to allow this compatibility mode and prevent this\n      // sequence from occurring.\n      default:\n        if ((isControlCharacter(*c)) || (*c == 0)) {\n          JSONCPP_OSTRINGSTREAM oss;\n          oss << \"\\\\u\" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);\n          result += oss.str();\n        } else {\n          result += *c;\n        }\n        break;\n    }\n  }\n  result += \"\\\"\";\n  return result;\n}\n\n// Class Writer\n// //////////////////////////////////////////////////////////////////\nWriter::~Writer() {}\n\n// Class FastWriter\n// //////////////////////////////////////////////////////////////////\n\nFastWriter::FastWriter() : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), omitEndingLineFeed_(false) {}\n\nvoid FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }\n\nvoid FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }\n\nvoid FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }\n\nJSONCPP_STRING FastWriter::write(const Value& root) {\n  document_ = \"\";\n  writeValue(root);\n  if (!omitEndingLineFeed_) document_ += \"\\n\";\n  return document_;\n}\n\nvoid FastWriter::writeValue(const Value& value) {\n  switch (value.type()) {\n    case nullValue:\n      if (!dropNullPlaceholders_) document_ += \"null\";\n      break;\n    case intValue:\n      document_ += valueToString(value.asLargestInt());\n      break;\n    case uintValue:\n      document_ += valueToString(value.asLargestUInt());\n      break;\n    case realValue:\n      document_ += valueToString(value.asDouble());\n      break;\n    case stringValue: {\n      // Is NULL possible for value.string_?\n      char const* str;\n      char const* end;\n      bool ok = value.getString(&str, &end);\n      if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));\n      break;\n    }\n    case booleanValue:\n      document_ += valueToString(value.asBool());\n      break;\n    case arrayValue: {\n      document_ += '[';\n      ArrayIndex size = value.size();\n      for (ArrayIndex index = 0; index < size; ++index) {\n        if (index > 0) document_ += ',';\n        writeValue(value[index]);\n      }\n      document_ += ']';\n    } break;\n    case objectValue: {\n      Value::Members members(value.getMemberNames());\n      document_ += '{';\n      for (Value::Members::iterator it = members.begin(); it != members.end(); ++it) {\n        const JSONCPP_STRING& name = *it;\n        if (it != members.begin()) document_ += ',';\n        document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));\n        document_ += yamlCompatiblityEnabled_ ? \": \" : \":\";\n        writeValue(value[name]);\n      }\n      document_ += '}';\n    } break;\n  }\n}\n\n// Class StyledWriter\n// //////////////////////////////////////////////////////////////////\n\nStyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3), addChildValues_() {}\n\nJSONCPP_STRING StyledWriter::write(const Value& root) {\n  document_ = \"\";\n  addChildValues_ = false;\n  indentString_ = \"\";\n  writeCommentBeforeValue(root);\n  writeValue(root);\n  writeCommentAfterValueOnSameLine(root);\n  document_ += \"\\n\";\n  return document_;\n}\n\nvoid StyledWriter::writeValue(const Value& value) {\n  switch (value.type()) {\n    case nullValue:\n      pushValue(\"null\");\n      break;\n    case intValue:\n      pushValue(valueToString(value.asLargestInt()));\n      break;\n    case uintValue:\n      pushValue(valueToString(value.asLargestUInt()));\n      break;\n    case realValue:\n      pushValue(valueToString(value.asDouble()));\n      break;\n    case stringValue: {\n      // Is NULL possible for value.string_?\n      char const* str;\n      char const* end;\n      bool ok = value.getString(&str, &end);\n      if (ok)\n        pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));\n      else\n        pushValue(\"\");\n      break;\n    }\n    case booleanValue:\n      pushValue(valueToString(value.asBool()));\n      break;\n    case arrayValue:\n      writeArrayValue(value);\n      break;\n    case objectValue: {\n      Value::Members members(value.getMemberNames());\n      if (members.empty())\n        pushValue(\"{}\");\n      else {\n        writeWithIndent(\"{\");\n        indent();\n        Value::Members::iterator it = members.begin();\n        for (;;) {\n          const JSONCPP_STRING& name = *it;\n          const Value& childValue = value[name];\n          writeCommentBeforeValue(childValue);\n          writeWithIndent(valueToQuotedString(name.c_str()));\n          document_ += \" : \";\n          writeValue(childValue);\n          if (++it == members.end()) {\n            writeCommentAfterValueOnSameLine(childValue);\n            break;\n          }\n          document_ += ',';\n          writeCommentAfterValueOnSameLine(childValue);\n        }\n        unindent();\n        writeWithIndent(\"}\");\n      }\n    } break;\n  }\n}\n\nvoid StyledWriter::writeArrayValue(const Value& value) {\n  unsigned size = value.size();\n  if (size == 0)\n    pushValue(\"[]\");\n  else {\n    bool isArrayMultiLine = isMultineArray(value);\n    if (isArrayMultiLine) {\n      writeWithIndent(\"[\");\n      indent();\n      bool hasChildValue = !childValues_.empty();\n      unsigned index = 0;\n      for (;;) {\n        const Value& childValue = value[index];\n        writeCommentBeforeValue(childValue);\n        if (hasChildValue)\n          writeWithIndent(childValues_[index]);\n        else {\n          writeIndent();\n          writeValue(childValue);\n        }\n        if (++index == size) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        document_ += ',';\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"]\");\n    } else  // output on a single line\n    {\n      assert(childValues_.size() == size);\n      document_ += \"[ \";\n      for (unsigned index = 0; index < size; ++index) {\n        if (index > 0) document_ += \", \";\n        document_ += childValues_[index];\n      }\n      document_ += \" ]\";\n    }\n  }\n}\n\nbool StyledWriter::isMultineArray(const Value& value) {\n  ArrayIndex const size = value.size();\n  bool isMultiLine = size * 3 >= rightMargin_;\n  childValues_.clear();\n  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {\n    const Value& childValue = value[index];\n    isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0);\n  }\n  if (!isMultiLine)  // check if line length > max line length\n  {\n    childValues_.reserve(size);\n    addChildValues_ = true;\n    ArrayIndex lineLength = 4 + (size - 1) * 2;  // '[ ' + ', '*n + ' ]'\n    for (ArrayIndex index = 0; index < size; ++index) {\n      if (hasCommentForValue(value[index])) {\n        isMultiLine = true;\n      }\n      writeValue(value[index]);\n      lineLength += static_cast<ArrayIndex>(childValues_[index].length());\n    }\n    addChildValues_ = false;\n    isMultiLine = isMultiLine || lineLength >= rightMargin_;\n  }\n  return isMultiLine;\n}\n\nvoid StyledWriter::pushValue(const JSONCPP_STRING& value) {\n  if (addChildValues_)\n    childValues_.push_back(value);\n  else\n    document_ += value;\n}\n\nvoid StyledWriter::writeIndent() {\n  if (!document_.empty()) {\n    char last = document_[document_.length() - 1];\n    if (last == ' ')  // already indented\n      return;\n    if (last != '\\n')  // Comments may add new-line\n      document_ += '\\n';\n  }\n  document_ += indentString_;\n}\n\nvoid StyledWriter::writeWithIndent(const JSONCPP_STRING& value) {\n  writeIndent();\n  document_ += value;\n}\n\nvoid StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); }\n\nvoid StyledWriter::unindent() {\n  assert(indentString_.size() >= indentSize_);\n  indentString_.resize(indentString_.size() - indentSize_);\n}\n\nvoid StyledWriter::writeCommentBeforeValue(const Value& root) {\n  if (!root.hasComment(commentBefore)) return;\n\n  document_ += \"\\n\";\n  writeIndent();\n  const JSONCPP_STRING& comment = root.getComment(commentBefore);\n  JSONCPP_STRING::const_iterator iter = comment.begin();\n  while (iter != comment.end()) {\n    document_ += *iter;\n    if (*iter == '\\n' && (iter != comment.end() && *(iter + 1) == '/')) writeIndent();\n    ++iter;\n  }\n\n  // Comments are stripped of trailing newlines, so add one here\n  document_ += \"\\n\";\n}\n\nvoid StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {\n  if (root.hasComment(commentAfterOnSameLine)) document_ += \" \" + root.getComment(commentAfterOnSameLine);\n\n  if (root.hasComment(commentAfter)) {\n    document_ += \"\\n\";\n    document_ += root.getComment(commentAfter);\n    document_ += \"\\n\";\n  }\n}\n\nbool StyledWriter::hasCommentForValue(const Value& value) {\n  return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter);\n}\n\n// Class StyledStreamWriter\n// //////////////////////////////////////////////////////////////////\n\nStyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation)\n    : document_(NULL), rightMargin_(74), indentation_(indentation), addChildValues_() {}\n\nvoid StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) {\n  document_ = &out;\n  addChildValues_ = false;\n  indentString_ = \"\";\n  indented_ = true;\n  writeCommentBeforeValue(root);\n  if (!indented_) writeIndent();\n  indented_ = true;\n  writeValue(root);\n  writeCommentAfterValueOnSameLine(root);\n  *document_ << \"\\n\";\n  document_ = NULL;  // Forget the stream, for safety.\n}\n\nvoid StyledStreamWriter::writeValue(const Value& value) {\n  switch (value.type()) {\n    case nullValue:\n      pushValue(\"null\");\n      break;\n    case intValue:\n      pushValue(valueToString(value.asLargestInt()));\n      break;\n    case uintValue:\n      pushValue(valueToString(value.asLargestUInt()));\n      break;\n    case realValue:\n      pushValue(valueToString(value.asDouble()));\n      break;\n    case stringValue: {\n      // Is NULL possible for value.string_?\n      char const* str;\n      char const* end;\n      bool ok = value.getString(&str, &end);\n      if (ok)\n        pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));\n      else\n        pushValue(\"\");\n      break;\n    }\n    case booleanValue:\n      pushValue(valueToString(value.asBool()));\n      break;\n    case arrayValue:\n      writeArrayValue(value);\n      break;\n    case objectValue: {\n      Value::Members members(value.getMemberNames());\n      if (members.empty())\n        pushValue(\"{}\");\n      else {\n        writeWithIndent(\"{\");\n        indent();\n        Value::Members::iterator it = members.begin();\n        for (;;) {\n          const JSONCPP_STRING& name = *it;\n          const Value& childValue = value[name];\n          writeCommentBeforeValue(childValue);\n          writeWithIndent(valueToQuotedString(name.c_str()));\n          *document_ << \" : \";\n          writeValue(childValue);\n          if (++it == members.end()) {\n            writeCommentAfterValueOnSameLine(childValue);\n            break;\n          }\n          *document_ << \",\";\n          writeCommentAfterValueOnSameLine(childValue);\n        }\n        unindent();\n        writeWithIndent(\"}\");\n      }\n    } break;\n  }\n}\n\nvoid StyledStreamWriter::writeArrayValue(const Value& value) {\n  unsigned size = value.size();\n  if (size == 0)\n    pushValue(\"[]\");\n  else {\n    bool isArrayMultiLine = isMultineArray(value);\n    if (isArrayMultiLine) {\n      writeWithIndent(\"[\");\n      indent();\n      bool hasChildValue = !childValues_.empty();\n      unsigned index = 0;\n      for (;;) {\n        const Value& childValue = value[index];\n        writeCommentBeforeValue(childValue);\n        if (hasChildValue)\n          writeWithIndent(childValues_[index]);\n        else {\n          if (!indented_) writeIndent();\n          indented_ = true;\n          writeValue(childValue);\n          indented_ = false;\n        }\n        if (++index == size) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        *document_ << \",\";\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"]\");\n    } else  // output on a single line\n    {\n      assert(childValues_.size() == size);\n      *document_ << \"[ \";\n      for (unsigned index = 0; index < size; ++index) {\n        if (index > 0) *document_ << \", \";\n        *document_ << childValues_[index];\n      }\n      *document_ << \" ]\";\n    }\n  }\n}\n\nbool StyledStreamWriter::isMultineArray(const Value& value) {\n  ArrayIndex const size = value.size();\n  bool isMultiLine = size * 3 >= rightMargin_;\n  childValues_.clear();\n  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {\n    const Value& childValue = value[index];\n    isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0);\n  }\n  if (!isMultiLine)  // check if line length > max line length\n  {\n    childValues_.reserve(size);\n    addChildValues_ = true;\n    ArrayIndex lineLength = 4 + (size - 1) * 2;  // '[ ' + ', '*n + ' ]'\n    for (ArrayIndex index = 0; index < size; ++index) {\n      if (hasCommentForValue(value[index])) {\n        isMultiLine = true;\n      }\n      writeValue(value[index]);\n      lineLength += static_cast<ArrayIndex>(childValues_[index].length());\n    }\n    addChildValues_ = false;\n    isMultiLine = isMultiLine || lineLength >= rightMargin_;\n  }\n  return isMultiLine;\n}\n\nvoid StyledStreamWriter::pushValue(const JSONCPP_STRING& value) {\n  if (addChildValues_)\n    childValues_.push_back(value);\n  else\n    *document_ << value;\n}\n\nvoid StyledStreamWriter::writeIndent() {\n  // blep intended this to look at the so-far-written string\n  // to determine whether we are already indented, but\n  // with a stream we cannot do that. So we rely on some saved state.\n  // The caller checks indented_.\n  *document_ << '\\n' << indentString_;\n}\n\nvoid StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) {\n  if (!indented_) writeIndent();\n  *document_ << value;\n  indented_ = false;\n}\n\nvoid StyledStreamWriter::indent() { indentString_ += indentation_; }\n\nvoid StyledStreamWriter::unindent() {\n  assert(indentString_.size() >= indentation_.size());\n  indentString_.resize(indentString_.size() - indentation_.size());\n}\n\nvoid StyledStreamWriter::writeCommentBeforeValue(const Value& root) {\n  if (!root.hasComment(commentBefore)) return;\n\n  if (!indented_) writeIndent();\n  const JSONCPP_STRING& comment = root.getComment(commentBefore);\n  JSONCPP_STRING::const_iterator iter = comment.begin();\n  while (iter != comment.end()) {\n    *document_ << *iter;\n    if (*iter == '\\n' && (iter != comment.end() && *(iter + 1) == '/'))\n      // writeIndent();  // would include newline\n      *document_ << indentString_;\n    ++iter;\n  }\n  indented_ = false;\n}\n\nvoid StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {\n  if (root.hasComment(commentAfterOnSameLine)) *document_ << ' ' << root.getComment(commentAfterOnSameLine);\n\n  if (root.hasComment(commentAfter)) {\n    writeIndent();\n    *document_ << root.getComment(commentAfter);\n  }\n  indented_ = false;\n}\n\nbool StyledStreamWriter::hasCommentForValue(const Value& value) {\n  return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter);\n}\n\n//////////////////////////\n// BuiltStyledStreamWriter\n\n/// Scoped enums are not available until C++11.\nstruct CommentStyle {\n  /// Decide whether to write comments.\n  enum Enum {\n    None,  ///< Drop all comments.\n    Most,  ///< Recover odd behavior of previous versions (not implemented yet).\n    All    ///< Keep all comments.\n  };\n};\n\nstruct BuiltStyledStreamWriter : public StreamWriter {\n  BuiltStyledStreamWriter(JSONCPP_STRING const& indentation, CommentStyle::Enum cs, JSONCPP_STRING const& colonSymbol,\n                          JSONCPP_STRING const& nullSymbol, JSONCPP_STRING const& endingLineFeedSymbol, bool useSpecialFloats,\n                          unsigned int precision);\n  int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;\n\n private:\n  void writeValue(Value const& value);\n  void writeArrayValue(Value const& value);\n  bool isMultineArray(Value const& value);\n  void pushValue(JSONCPP_STRING const& value);\n  void writeIndent();\n  void writeWithIndent(JSONCPP_STRING const& value);\n  void indent();\n  void unindent();\n  void writeCommentBeforeValue(Value const& root);\n  void writeCommentAfterValueOnSameLine(Value const& root);\n  static bool hasCommentForValue(const Value& value);\n\n  typedef std::vector<JSONCPP_STRING> ChildValues;\n\n  ChildValues childValues_;\n  JSONCPP_STRING indentString_;\n  unsigned int rightMargin_;\n  JSONCPP_STRING indentation_;\n  CommentStyle::Enum cs_;\n  JSONCPP_STRING colonSymbol_;\n  JSONCPP_STRING nullSymbol_;\n  JSONCPP_STRING endingLineFeedSymbol_;\n  bool addChildValues_ : 1;\n  bool indented_ : 1;\n  bool useSpecialFloats_ : 1;\n  unsigned int precision_;\n};\nBuiltStyledStreamWriter::BuiltStyledStreamWriter(JSONCPP_STRING const& indentation, CommentStyle::Enum cs, JSONCPP_STRING const& colonSymbol,\n                                                 JSONCPP_STRING const& nullSymbol, JSONCPP_STRING const& endingLineFeedSymbol, bool useSpecialFloats,\n                                                 unsigned int precision)\n    : rightMargin_(74),\n      indentation_(indentation),\n      cs_(cs),\n      colonSymbol_(colonSymbol),\n      nullSymbol_(nullSymbol),\n      endingLineFeedSymbol_(endingLineFeedSymbol),\n      addChildValues_(false),\n      indented_(false),\n      useSpecialFloats_(useSpecialFloats),\n      precision_(precision) {}\nint BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) {\n  sout_ = sout;\n  addChildValues_ = false;\n  indented_ = true;\n  indentString_ = \"\";\n  writeCommentBeforeValue(root);\n  if (!indented_) writeIndent();\n  indented_ = true;\n  writeValue(root);\n  writeCommentAfterValueOnSameLine(root);\n  *sout_ << endingLineFeedSymbol_;\n  sout_ = NULL;\n  return 0;\n}\nvoid BuiltStyledStreamWriter::writeValue(Value const& value) {\n  switch (value.type()) {\n    case nullValue:\n      pushValue(nullSymbol_);\n      break;\n    case intValue:\n      pushValue(valueToString(value.asLargestInt()));\n      break;\n    case uintValue:\n      pushValue(valueToString(value.asLargestUInt()));\n      break;\n    case realValue:\n      pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));\n      break;\n    case stringValue: {\n      // Is NULL is possible for value.string_?\n      char const* str;\n      char const* end;\n      bool ok = value.getString(&str, &end);\n      if (ok)\n        pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));\n      else\n        pushValue(\"\");\n      break;\n    }\n    case booleanValue:\n      pushValue(valueToString(value.asBool()));\n      break;\n    case arrayValue:\n      writeArrayValue(value);\n      break;\n    case objectValue: {\n      Value::Members members(value.getMemberNames());\n      if (members.empty())\n        pushValue(\"{}\");\n      else {\n        writeWithIndent(\"{\");\n        indent();\n        Value::Members::iterator it = members.begin();\n        for (;;) {\n          JSONCPP_STRING const& name = *it;\n          Value const& childValue = value[name];\n          writeCommentBeforeValue(childValue);\n          writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));\n          *sout_ << colonSymbol_;\n          writeValue(childValue);\n          if (++it == members.end()) {\n            writeCommentAfterValueOnSameLine(childValue);\n            break;\n          }\n          *sout_ << \",\";\n          writeCommentAfterValueOnSameLine(childValue);\n        }\n        unindent();\n        writeWithIndent(\"}\");\n      }\n    } break;\n  }\n}\n\nvoid BuiltStyledStreamWriter::writeArrayValue(Value const& value) {\n  unsigned size = value.size();\n  if (size == 0)\n    pushValue(\"[]\");\n  else {\n    bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);\n    if (isMultiLine) {\n      writeWithIndent(\"[\");\n      indent();\n      bool hasChildValue = !childValues_.empty();\n      unsigned index = 0;\n      for (;;) {\n        Value const& childValue = value[index];\n        writeCommentBeforeValue(childValue);\n        if (hasChildValue)\n          writeWithIndent(childValues_[index]);\n        else {\n          if (!indented_) writeIndent();\n          indented_ = true;\n          writeValue(childValue);\n          indented_ = false;\n        }\n        if (++index == size) {\n          writeCommentAfterValueOnSameLine(childValue);\n          break;\n        }\n        *sout_ << \",\";\n        writeCommentAfterValueOnSameLine(childValue);\n      }\n      unindent();\n      writeWithIndent(\"]\");\n    } else  // output on a single line\n    {\n      assert(childValues_.size() == size);\n      *sout_ << \"[\";\n      if (!indentation_.empty()) *sout_ << \" \";\n      for (unsigned index = 0; index < size; ++index) {\n        if (index > 0) *sout_ << \", \";\n        *sout_ << childValues_[index];\n      }\n      if (!indentation_.empty()) *sout_ << \" \";\n      *sout_ << \"]\";\n    }\n  }\n}\n\nbool BuiltStyledStreamWriter::isMultineArray(Value const& value) {\n  ArrayIndex const size = value.size();\n  bool isMultiLine = size * 3 >= rightMargin_;\n  childValues_.clear();\n  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {\n    Value const& childValue = value[index];\n    isMultiLine = ((childValue.isArray() || childValue.isObject()) && childValue.size() > 0);\n  }\n  if (!isMultiLine)  // check if line length > max line length\n  {\n    childValues_.reserve(size);\n    addChildValues_ = true;\n    ArrayIndex lineLength = 4 + (size - 1) * 2;  // '[ ' + ', '*n + ' ]'\n    for (ArrayIndex index = 0; index < size; ++index) {\n      if (hasCommentForValue(value[index])) {\n        isMultiLine = true;\n      }\n      writeValue(value[index]);\n      lineLength += static_cast<ArrayIndex>(childValues_[index].length());\n    }\n    addChildValues_ = false;\n    isMultiLine = isMultiLine || lineLength >= rightMargin_;\n  }\n  return isMultiLine;\n}\n\nvoid BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) {\n  if (addChildValues_)\n    childValues_.push_back(value);\n  else\n    *sout_ << value;\n}\n\nvoid BuiltStyledStreamWriter::writeIndent() {\n  // blep intended this to look at the so-far-written string\n  // to determine whether we are already indented, but\n  // with a stream we cannot do that. So we rely on some saved state.\n  // The caller checks indented_.\n\n  if (!indentation_.empty()) {\n    // In this case, drop newlines too.\n    *sout_ << '\\n' << indentString_;\n  }\n}\n\nvoid BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) {\n  if (!indented_) writeIndent();\n  *sout_ << value;\n  indented_ = false;\n}\n\nvoid BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }\n\nvoid BuiltStyledStreamWriter::unindent() {\n  assert(indentString_.size() >= indentation_.size());\n  indentString_.resize(indentString_.size() - indentation_.size());\n}\n\nvoid BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {\n  if (cs_ == CommentStyle::None) return;\n  if (!root.hasComment(commentBefore)) return;\n\n  if (!indented_) writeIndent();\n  const JSONCPP_STRING& comment = root.getComment(commentBefore);\n  JSONCPP_STRING::const_iterator iter = comment.begin();\n  while (iter != comment.end()) {\n    *sout_ << *iter;\n    if (*iter == '\\n' && (iter != comment.end() && *(iter + 1) == '/'))\n      // writeIndent();  // would write extra newline\n      *sout_ << indentString_;\n    ++iter;\n  }\n  indented_ = false;\n}\n\nvoid BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {\n  if (cs_ == CommentStyle::None) return;\n  if (root.hasComment(commentAfterOnSameLine)) *sout_ << \" \" + root.getComment(commentAfterOnSameLine);\n\n  if (root.hasComment(commentAfter)) {\n    writeIndent();\n    *sout_ << root.getComment(commentAfter);\n  }\n}\n\n// static\nbool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {\n  return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) || value.hasComment(commentAfter);\n}\n\n///////////////\n// StreamWriter\n\nStreamWriter::StreamWriter() : sout_(NULL) {}\nStreamWriter::~StreamWriter() {}\nStreamWriter::Factory::~Factory() {}\nStreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }\nStreamWriterBuilder::~StreamWriterBuilder() {}\nStreamWriter* StreamWriterBuilder::newStreamWriter() const {\n  JSONCPP_STRING indentation = settings_[\"indentation\"].asString();\n  JSONCPP_STRING cs_str = settings_[\"commentStyle\"].asString();\n  bool eyc = settings_[\"enableYAMLCompatibility\"].asBool();\n  bool dnp = settings_[\"dropNullPlaceholders\"].asBool();\n  bool usf = settings_[\"useSpecialFloats\"].asBool();\n  unsigned int pre = settings_[\"precision\"].asUInt();\n  CommentStyle::Enum cs = CommentStyle::All;\n  if (cs_str == \"All\") {\n    cs = CommentStyle::All;\n  } else if (cs_str == \"None\") {\n    cs = CommentStyle::None;\n  } else {\n    throwRuntimeError(\"commentStyle must be 'All' or 'None'\");\n  }\n  JSONCPP_STRING colonSymbol = \" : \";\n  if (eyc) {\n    colonSymbol = \": \";\n  } else if (indentation.empty()) {\n    colonSymbol = \":\";\n  }\n  JSONCPP_STRING nullSymbol = \"null\";\n  if (dnp) {\n    nullSymbol = \"\";\n  }\n  if (pre > 17) pre = 17;\n  JSONCPP_STRING endingLineFeedSymbol = \"\";\n  return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);\n}\nstatic void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys) {\n  valid_keys->clear();\n  valid_keys->insert(\"indentation\");\n  valid_keys->insert(\"commentStyle\");\n  valid_keys->insert(\"enableYAMLCompatibility\");\n  valid_keys->insert(\"dropNullPlaceholders\");\n  valid_keys->insert(\"useSpecialFloats\");\n  valid_keys->insert(\"precision\");\n}\nbool StreamWriterBuilder::validate(Json::Value* invalid) const {\n  Json::Value my_invalid;\n  if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL\n  Json::Value& inv = *invalid;\n  std::set<JSONCPP_STRING> valid_keys;\n  getValidWriterKeys(&valid_keys);\n  Value::Members keys = settings_.getMemberNames();\n  size_t n = keys.size();\n  for (size_t i = 0; i < n; ++i) {\n    JSONCPP_STRING const& key = keys[i];\n    if (valid_keys.find(key) == valid_keys.end()) {\n      inv[key] = settings_[key];\n    }\n  }\n  return 0u == inv.size();\n}\nValue& StreamWriterBuilder::operator[](JSONCPP_STRING key) { return settings_[key]; }\n// static\nvoid StreamWriterBuilder::setDefaults(Json::Value* settings) {\n  //! [StreamWriterBuilderDefaults]\n  (*settings)[\"commentStyle\"] = \"All\";\n  (*settings)[\"indentation\"] = \"\\t\";\n  (*settings)[\"enableYAMLCompatibility\"] = false;\n  (*settings)[\"dropNullPlaceholders\"] = false;\n  (*settings)[\"useSpecialFloats\"] = false;\n  (*settings)[\"precision\"] = 17;\n  //! [StreamWriterBuilderDefaults]\n}\n\nJSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) {\n  JSONCPP_OSTRINGSTREAM sout;\n  StreamWriterPtr const writer(builder.newStreamWriter());\n  writer->write(root, &sout);\n  return sout.str();\n}\n\nJSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) {\n  StreamWriterBuilder builder;\n  StreamWriterPtr const writer(builder.newStreamWriter());\n  writer->write(root, &sout);\n  return sout;\n}\n\n}  // namespace Json\n\n// //////////////////////////////////////////////////////////////////////\n// End of content of file: src/lib_json/json_writer.cpp\n// //////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "examples/websocket_client/testclient.cpp",
    "content": "/**\n * Simple WebRTC test client.\n */\n\n#include \"WebSocketWrapper.hpp\"\n#include \"json/json.h\"\n\n#include <rtcdcpp/PeerConnection.hpp>\n#include <rtcdcpp/Logging.hpp>\n\n#include <ios>\n#include <iostream>\n#include <fstream>\n\nusing namespace rtcdcpp;\n\nvoid send_loop(std::shared_ptr<DataChannel> dc) {\n  std::ifstream bunnyFile;\n  bunnyFile.open(\"frag_bunny.mp4\", std::ios_base::in | std::ios_base::binary);\n\n  char buf[100 * 1024];\n\n  while (bunnyFile.good()) {\n    bunnyFile.read(buf, 100 * 1024);\n    int nRead = bunnyFile.gcount();\n    if (nRead > 0) {\n      dc->SendBinary((const uint8_t *)buf, nRead);\n      std::this_thread::sleep_for(std::chrono::seconds(1));\n    }\n\n    std::cout << \"Sent message of size \" << std::to_string(nRead) << std::endl;\n  }\n}\n\nint main() {\n#ifndef SPDLOG_DISABLED\n  auto console_sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();\n  spdlog::logger(\"rtcdcpp.PeerConnection\", console_sink);\n  spdlog::logger(\"rtcdcpp.SCTP\", console_sink);\n  spdlog::logger(\"rtcdcpp.Nice\", console_sink);\n  spdlog::logger(\"rtcdcpp.DTLS\", console_sink);\n  spdlog::set_level(spdlog::level::debug);\n#endif\n\n  WebSocketWrapper ws(\"ws://localhost:5000/channel/test\");\n  std::shared_ptr<PeerConnection> pc;\n  std::shared_ptr<DataChannel> dc;\n\n  if (!ws.Initialize()) {\n    std::cout << \"WebSocket connection failed\\n\";\n    return 0;\n  }\n\n  RTCConfiguration config;\n  config.ice_servers.emplace_back(RTCIceServer{\"stun3.l.google.com\", 19302});\n\n  bool running = true;\n\n  ChunkQueue messages;\n\n  std::function<void(std::string)> onMessage = [&messages](std::string msg) {\n    messages.push(std::shared_ptr<Chunk>(new Chunk((const void *)msg.c_str(), msg.length())));\n  };\n\n  std::function<void(PeerConnection::IceCandidate)> onLocalIceCandidate = [&ws](PeerConnection::IceCandidate candidate) {\n    Json::Value jsonCandidate;\n    jsonCandidate[\"type\"] = \"candidate\";\n    jsonCandidate[\"msg\"][\"candidate\"] = candidate.candidate;\n    jsonCandidate[\"msg\"][\"sdpMid\"] = candidate.sdpMid;\n    jsonCandidate[\"msg\"][\"sdpMLineIndex\"] = candidate.sdpMLineIndex;\n\n    Json::StreamWriterBuilder wBuilder;\n    ws.Send(Json::writeString(wBuilder, jsonCandidate));\n  };\n\n  std::function<void(std::shared_ptr<DataChannel> channel)> onDataChannel = [&dc](std::shared_ptr<DataChannel> channel) {\n    std::cout << \"Hey cool, got a data channel\\n\";\n    dc = channel;\n    std::thread send_thread = std::thread(send_loop, channel);\n    send_thread.detach();\n  };\n\n  ws.SetOnMessage(onMessage);\n  ws.Start();\n  ws.Send(\"{\\\"type\\\": \\\"client_connected\\\", \\\"msg\\\": {}}\");\n\n  Json::Reader reader;\n  Json::StreamWriterBuilder msgBuilder;\n\n  while (running) {\n    ChunkPtr cur_msg = messages.wait_and_pop();\n    std::string msg((const char *)cur_msg->Data(), cur_msg->Length());\n    std::cout << msg << \"\\n\";\n    Json::Value root;\n    if (reader.parse(msg, root)) {\n      std::cout << \"Got msg of type: \" << root[\"type\"] << \"\\n\";\n      if (root[\"type\"] == \"offer\") {\n        std::cout << \"Time to get the rtc party started\\n\";\n        pc = std::make_shared<PeerConnection>(config, onLocalIceCandidate, onDataChannel);\n\n        pc->ParseOffer(root[\"msg\"][\"sdp\"].asString());\n        Json::Value answer;\n        answer[\"type\"] = \"answer\";\n        answer[\"msg\"][\"sdp\"] = pc->GenerateAnswer();\n        answer[\"msg\"][\"type\"] = \"answer\";\n\n        std::cout << \"Sending Answer: \" << answer << \"\\n\";\n        ws.Send(Json::writeString(msgBuilder, answer));\n      } else if (root[\"type\"] == \"candidate\") {\n        pc->SetRemoteIceCandidate(\"a=\" + root[\"msg\"][\"candidate\"].asString());\n      }\n    } else {\n      std::cout << \"Json parse failed\"\n                << \"\\n\";\n    }\n  }\n\n  ws.Close();\n\n  return 0;\n}\n"
  },
  {
    "path": "include/rtcdcpp/Chunk.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <memory>\n\n#include <cstring>\n#include <condition_variable>\nnamespace rtcdcpp {\n\n// Utility class for passing messages around\nclass Chunk {\n private:\n  size_t len{0};\n  uint8_t *data{nullptr};\n\n public:\n  // TODO memory pool?\n  // XXX should we just use a vector?\n\n  // Makes a copy of data\n  Chunk(const void *dataToCopy, size_t dataLen) : len(dataLen), data(new uint8_t[len]) { memcpy(data, dataToCopy, dataLen); }\n\n  // Copy constructor\n  Chunk(const Chunk &other) : len(other.len), data(new uint8_t[len]) { memcpy(data, other.data, other.len); }\n\n  // Assignment operator\n  Chunk &operator=(const Chunk &other) {\n    if (data) {\n      len = 0;\n      delete[] data;\n    }\n    len = other.len;\n    data = new uint8_t[len];\n    memcpy(data, other.data, other.len);\n    return *this;\n  }\n\n  ~Chunk() { delete[] data; }\n\n  size_t Size() const { return len; }\n  size_t Length() const { return Size(); }\n  uint8_t *Data() const { return data; }\n};\n\nusing ChunkPtr = std::shared_ptr<Chunk>;\n}\n"
  },
  {
    "path": "include/rtcdcpp/ChunkQueue.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Simple blocking thread-safe queue.\n */\n\n#pragma once\n\n#include \"Chunk.hpp\"\n\n#include <mutex>\n#include <queue>\n\nnamespace rtcdcpp {\n\n/**\n * Thread-Safe Queue of DataChunks\n */\nclass ChunkQueue {\n private:\n  mutable std::mutex mut;\n  std::queue<ChunkPtr> chunk_queue;\n  std::condition_variable data_cond;\n  bool stopping;\n\n public:\n  ChunkQueue() : chunk_queue(), stopping(false) {}\n\n  void Stop() {\n    std::lock_guard<std::mutex> lock(mut);\n    stopping = true;\n    data_cond.notify_all();\n  }\n\n  void push(ChunkPtr chunk) {\n    std::lock_guard<std::mutex> lock(mut);\n    if (stopping) {\n      return;\n    }\n    chunk_queue.push(chunk);\n    data_cond.notify_one();\n  }\n\n  ChunkPtr wait_and_pop() {\n    std::unique_lock<std::mutex> lock(mut);\n    while (!stopping && chunk_queue.empty()) {\n      data_cond.wait(lock);\n    }\n\n    if (stopping) {\n      return ChunkPtr();\n    }\n\n    ChunkPtr res = chunk_queue.front();\n    chunk_queue.pop();\n    return res;\n  }\n\n  bool empty() const {\n    std::lock_guard<std::mutex> lock(mut);\n    return chunk_queue.empty();\n  }\n};\n}\n"
  },
  {
    "path": "include/rtcdcpp/DTLSWrapper.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#pragma once\n\n/**\n * Wrapper around OpenSSL DTLS.\n */\n\n#include \"ChunkQueue.hpp\"\n#include \"PeerConnection.hpp\"\n#include \"Logging.hpp\"\n\n#include <openssl/ssl.h>\n\n#include <thread>\n\nnamespace rtcdcpp {\n\nclass DTLSWrapper {\n public:\n  DTLSWrapper(PeerConnection *peer_connection);\n  virtual ~DTLSWrapper();\n\n  const RTCCertificate *certificate() { return certificate_; }\n\n  bool Initialize();\n  void Start();\n  void Stop();\n\n  void EncryptData(ChunkPtr chunk);\n  void DecryptData(ChunkPtr chunk);\n\n  void SetEncryptedCallback(std::function<void(ChunkPtr chunk)>);\n  void SetDecryptedCallback(std::function<void(ChunkPtr chunk)>);\n\n private:\n  PeerConnection *peer_connection;\n  const RTCCertificate *certificate_;\n\n  std::atomic<bool> should_stop;\n\n  ChunkQueue encrypt_queue;\n  ChunkQueue decrypt_queue;\n\n  std::thread encrypt_thread;\n  std::thread decrypt_thread;\n\n  void RunEncrypt();\n  void RunDecrypt();\n\n  // SSL Context\n  std::mutex ssl_mutex;\n  SSL_CTX *ctx;\n  SSL *ssl;\n  BIO *in_bio, *out_bio;\n\n  bool handshake_complete;\n\n  std::function<void(ChunkPtr chunk)> decrypted_callback;\n  std::function<void(ChunkPtr chunk)> encrypted_callback;\n\n  std::shared_ptr<Logger> logger = GetLogger(\"rtcdcpp.DTLS\");\n};\n}\n"
  },
  {
    "path": "include/rtcdcpp/DataChannel.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * WebRTC DataChannel.\n*/\n\n#pragma once\n\n#include \"Chunk.hpp\"\n#include <functional>\n#include <string>\n\nnamespace rtcdcpp {\n\n// SCTP PPID Types\n#define PPID_CONTROL 50\n#define PPID_STRING 51\n#define PPID_BINARY 53\n#define PPID_STRING_EMPTY 56\n#define PPID_BINARY_EMPTY 57\n\n// DataChannel Control Types\n#define DC_TYPE_OPEN 0x03\n#define DC_TYPE_ACK 0x02\n\n// Channel types\n#define DATA_CHANNEL_RELIABLE 0x00\n#define DATA_CHANNEL_RELIABLE_UNORDERED 0x80\n#define DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT 0x01\n#define DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED 0x81\n#define DATA_CHANNEL_PARTIAL_RELIABLE_TIMED 0x02\n#define DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED 0x82\n\ntypedef struct {\n  uint8_t msg_type;\n  uint8_t chan_type;\n  uint16_t priority;\n  uint32_t reliability;\n  uint16_t label_len;\n  uint16_t protocol_len;\n  char *label;\n  char *protocol;\n} dc_open_msg;\n\ntypedef struct { uint8_t msg_type; } dc_open_ack;\n\nclass PeerConnection;\n\nclass DataChannel {\n  friend class PeerConnection;\n\n private:\n  PeerConnection *pc;\n  uint16_t stream_id;\n  uint8_t chan_type;\n  std::string label;\n  std::string protocol;\n\n  // TODO: Priority field\n\n  std::function<void()> open_cb;\n  std::function<void(std::string)> str_msg_cb;\n  // std::function<void(std::shared_ptr<uint8_t> data, int len)> bin_msg_cb;\n  std::function<void(ChunkPtr)> bin_msg_cb;\n  std::function<void()> closed_cb;\n  std::function<void(std::string description)> error_cb;\n\n  void OnOpen();\n  void OnStringMsg(std::string msg);\n  void OnBinaryMsg(ChunkPtr msg);\n  void OnClosed();\n  void OnError(std::string description);\n\n public:\n  DataChannel(PeerConnection *pc, uint16_t stream_id, uint8_t chan_type, std::string label, std::string protocol);\n  virtual ~DataChannel();\n\n  /**\n   * Get the Stream ID for the DataChannel.\n   * XXX: Stream IDs *are* unique.\n   */\n  uint16_t GetStreamID();\n\n  /**\n   * Get the channel type.\n   */\n  uint8_t GetChannelType();\n\n  /**\n   * Get the label for the DataChannel.\n   * XXX: Labels are *not* unique.\n   */\n  std::string GetLabel();\n\n  /**\n   * Get the protocol for the DataChannel.\n   */\n  std::string GetProtocol();\n\n  /**\n   * Cleanly close the DataChannel.\n   */\n  void Close();\n\n  /**\n   * Send calls return false if the DataChannel is no longer operational,\n   * ie. an error or close event has been detected.\n   */\n  bool SendString(std::string msg);\n  bool SendBinary(const uint8_t *msg, int len);\n\n  // Callbacks\n\n  /**\n   * Called when the remote peer 'acks' our data channel\n   * This is only called when we were the peer who created the data channel.\n   * Receiving this message means its 'safe' to send messages, but messages\n   * can be sent before this is received (its just unknown if they'll arrive).\n   */\n  void SetOnOpen(std::function<void()> open_cb);\n\n  /**\n   * Called when we receive a string.\n   */\n  void SetOnStringMsgCallback(std::function<void(std::string)> recv_str_cb);\n\n  /**\n   * Called when we receive a binary blob.\n   */\n  void SetOnBinaryMsgCallback(std::function<void(ChunkPtr)> msg_binary_cb);\n\n  /**\n   * Called when the DataChannel has been cleanly closed.\n   * NOT called after the Close() method has been called\n   * NOT called after an error has been received.\n   */\n  void SetOnClosedCallback(std::function<void()> close_cb);\n\n  /**\n   * Called when there has been an error in the underlying transport and the\n   * data channel is no longer valid.\n   */\n  void SetOnErrorCallback(std::function<void(std::string description)> error_cb);\n};\n}\n"
  },
  {
    "path": "include/rtcdcpp/Logging.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#pragma once\n\n#include <memory>\n\n#ifndef SPDLOG_DISABLED\n#include <spdlog/spdlog.h>\n#include \"spdlog/sinks/stdout_color_sinks.h\"\n#include <spdlog/fmt/ostr.h>\n#endif\n\nnamespace rtcdcpp {\n\n#ifndef SPDLOG_DISABLED\n\ntypedef spdlog::logger Logger;\n\n#else\n\nclass Logger {\n public:\n\n  Logger() = default;\n\n  Logger(const Logger &) = delete;\n  void operator=(const Logger &) = delete;\n  Logger(Logger &&) = delete;\n  void operator=(Logger &&) = delete;\n\n  template<typename... Args>\n  void trace(const char *fmt, const Args &... args) {}\n  template<typename... Args>\n  void debug(const char *fmt, const Args &... args) {}\n  template<typename... Args>\n  void info(const char *fmt, const Args &... args) {}\n  template<typename... Args>\n  void warn(const char *fmt, const Args &... args) {}\n  template<typename... Args>\n  void error(const char *fmt, const Args &... args) {}\n  template<typename... Args>\n  void critical(const char *fmt, const Args &... args) {}\n\n  template<typename T>\n  void trace(const T &) {}\n  template<typename T>\n  void debug(const T &) {}\n  template<typename T>\n  void info(const T &) {}\n  template<typename T>\n  void warn(const T &) {}\n  template<typename T>\n  void error(const T &) {}\n  template<typename T>\n  void critical(const T &) {}\n};\n\n#define SPDLOG_TRACE(logger, ...)\n#define SPDLOG_DEBUG(logger, ...)\n\n#endif\n\nstd::shared_ptr<Logger> GetLogger(const std::string &logger_name);\n\n}\n"
  },
  {
    "path": "include/rtcdcpp/NiceWrapper.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#pragma once\n\n/**\n * Wrapper around libnice and NiceAgent.\n */\n\n#include \"ChunkQueue.hpp\"\n#include \"PeerConnection.hpp\"\n#include \"Logging.hpp\"\n\n#include <thread>\n\nextern \"C\" {\n#include <nice/agent.h>\n}\n\nnamespace rtcdcpp {\n\n/**\n * Nice Wrapper broh.\n */\nclass NiceWrapper {\n public:\n  // TODO: Remove reference to handler\n  // TODO: Add callback for candidates\n  NiceWrapper(PeerConnection *peer_connection);\n  virtual ~NiceWrapper();\n\n  // Setup libnice\n  bool Initialize();\n\n  // Start sending packets XXX: recv just happens once candidates are set\n  void StartSendLoop();\n\n  // Shutdown nice and stop the send thread\n  void Stop();\n\n  // Parse the remote SDP\n  void ParseRemoteSDP(std::string remote_sdp);\n\n  // void SetRemoteCredentials(std::string username, std::string password);\n\n  // Generate the local SDP\n  std::string GenerateLocalSDP();\n\n  // Add a single remote ice candidate (supports trickling)\n  bool SetRemoteIceCandidate(std::string candidate_sdp);\n\n  // Set the remote ice candidates\n  bool SetRemoteIceCandidates(std::vector<std::string> candidate_sdps);\n\n  // Callback to call when we receive local ice candidates\n  // void SetLocalCandidatesCallback(std::vector<std::string> candidate_sdps);\n\n  // Callback to call when we receive remote data\n  void SetDataReceivedCallback(std::function<void(ChunkPtr)>);\n\n  // Send data over the nice channel\n  void SendData(ChunkPtr chunk);\n\n private:\n  PeerConnection *peer_connection;\n  int packets_sent;\n\n  std::unique_ptr<NiceAgent, void (*)(gpointer)> agent;\n  std::unique_ptr<GMainLoop, void (*)(GMainLoop *)> loop;\n  uint32_t stream_id;\n  std::mutex send_lock;\n\n  bool gathering_done;\n  bool negotiation_done;\n\n  ChunkQueue send_queue;\n\n  std::function<void(ChunkPtr)> data_received_callback;\n\n  // Send data thread\n  void SendLoop();\n  std::thread send_thread;\n  std::thread g_main_loop_thread;\n  std::atomic<bool> should_stop;\n\n  // Callback methods\n  void OnStateChange(uint32_t stream_id, uint32_t component_id, uint32_t state);\n  void OnGatheringDone();\n  void OnCandidate(std::string candidate);\n  void OnSelectedPair();\n  void OnDataReceived(const uint8_t *buf, int len);\n  void OnIceReady();\n  void LogMessage(const gchar *message);\n\n  // Helper functions\n  friend void candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer user_data);\n  friend void component_state_changed(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer user_data);\n  friend void new_local_candidate(NiceAgent *agent, NiceCandidate *candidate, gpointer user_data);\n  friend void new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *lcandidate, NiceCandidate *rcandidate,\n                                gpointer user_data);\n  friend void data_received(NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data);\n  friend void nice_log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data);\n\n  std::shared_ptr<Logger> logger = GetLogger(\"rtcdcpp.Nice\");\n};\n}\n"
  },
  {
    "path": "include/rtcdcpp/PeerConnection.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#pragma once\n\n#include \"ChunkQueue.hpp\"\n#include \"DataChannel.hpp\"\n#include \"RTCCertificate.hpp\"\n#include \"Logging.hpp\"\n#include <atomic>\n#include <map>\n\nnamespace rtcdcpp {\n\nclass NiceWrapper;\nclass DTLSWrapper;\nclass SCTPWrapper;\n\nstruct RTCIceServer {\n  std::string hostname;\n  int port;\n};\n\nstd::ostream &operator<<(std::ostream &os, const RTCIceServer &ice_server);\n\nstruct RTCConfiguration {\n  std::vector<RTCIceServer> ice_servers;\n  std::pair<unsigned, unsigned> ice_port_range;\n  std::string ice_ufrag;\n  std::string ice_pwd;\n  std::vector<RTCCertificate> certificates;\n};\n\nclass PeerConnection {\n public:\n  struct IceCandidate {\n    IceCandidate(const std::string &candidate, const std::string &sdpMid, int sdpMLineIndex)\n        : candidate(candidate), sdpMid(sdpMid), sdpMLineIndex(sdpMLineIndex) {}\n    std::string candidate;\n    std::string sdpMid;\n    int sdpMLineIndex;\n  };\n\n  using IceCandidateCallbackPtr = std::function<void(IceCandidate)>;\n  using DataChannelCallbackPtr = std::function<void(std::shared_ptr<DataChannel> channel)>;\n\n  PeerConnection(const RTCConfiguration &config, IceCandidateCallbackPtr icCB, DataChannelCallbackPtr dcCB);\n\n  virtual ~PeerConnection();\n\n  const RTCConfiguration &config() { return config_; }\n\n  /**\n   *\n   * Parse Offer SDP\n   */\n  void ParseOffer(std::string offer_sdp);\n\n  /**\n   * Generate Answer SDP\n   */\n  std::string GenerateAnswer();\n\n  /**\n  * Handle remote ICE Candidate.\n  * Supports trickle ice candidates.\n  */\n  bool SetRemoteIceCandidate(std::string candidate_sdp);\n\n  /**\n  * Handle remote ICE Candidates.\n  * TODO: Handle trickle ice candidates.\n  */\n  bool SetRemoteIceCandidates(std::vector<std::string> candidate_sdps);\n\n  /**\n   * Create a new data channel with the given label.\n   * Only callable once RTCConnectedCallback has been called.\n   * TODO: Handle creating data channels before generating SDP, so that the\n   *       data channel is created as part of the connection process.\n   */\n  //    std::shared_ptr<DataChannel> CreateDataChannel(std::string label);\n\n  /**\n   * Notify when remote party creates a DataChannel.\n   * XXX: This is *not* a callback saying that a call to CreateDataChannel\n   *      has succeeded. This is a call saying the remote party wants to\n   *      create a new data channel.\n   */\n  //\tvoid SetDataChannelCreatedCallback(DataChannelCallbackPtr cb);\n\n  // TODO: Error callbacks\n\n  void SendStrMsg(std::string msg, uint16_t sid);\n  void SendBinaryMsg(const uint8_t *data, int len, uint16_t sid);\n\n  /* Internal Callback Handlers */\n  void OnLocalIceCandidate(std::string &ice_candidate);\n  void OnIceReady();\n  void OnDTLSHandshakeDone();\n  void OnSCTPMsgReceived(ChunkPtr chunk, uint16_t sid, uint32_t ppid);\n\n private:\n  RTCConfiguration config_;\n  const IceCandidateCallbackPtr ice_candidate_cb;\n  const DataChannelCallbackPtr new_channel_cb;\n\n  std::string mid;\n\n  enum Role { Client, Server } role = Client;\n\n  std::atomic<bool> iceReady{false};\n  std::unique_ptr<NiceWrapper> nice;\n  std::unique_ptr<DTLSWrapper> dtls;\n  std::unique_ptr<SCTPWrapper> sctp;\n\n  std::map<uint16_t, std::shared_ptr<DataChannel>> data_channels;\n  std::shared_ptr<DataChannel> GetChannel(uint16_t sid);\n\n  /**\n  * Constructor helper\n  * Initialize the RTC connection.\n  * Allocates all internal structures and configs, and starts ICE gathering.\n  */\n  bool Initialize();\n\n  // DataChannel message parsing\n  void HandleNewDataChannel(ChunkPtr chunk, uint16_t sid);\n  void HandleStringMessage(ChunkPtr chunk, uint16_t sid);\n  void HandleBinaryMessage(ChunkPtr chunk, uint16_t sid);\n\n  std::shared_ptr<Logger> logger = GetLogger(\"rtcdcpp.PeerConnection\");\n\n};\n}\n"
  },
  {
    "path": "include/rtcdcpp/RTCCertificate.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#pragma once\n\n/**\n * Wrapper around OpenSSL Certs.\n */\n\n#include <openssl/x509.h>\n#include <memory>\n#include <cstring>\n#include <string>\n\nnamespace rtcdcpp {\n\n#define SHA256_FINGERPRINT_SIZE (95 + 1)\n\nclass RTCCertificate {\n public:\n  static RTCCertificate GenerateCertificate(std::string common_name, int days);\n\n  RTCCertificate(std::string cert_pem, std::string pkey_pem);\n\n  const std::string &fingerprint() const { return fingerprint_; }\n\n protected:\n  friend class DTLSWrapper;\n\n  X509 *x509() const { return x509_.get(); }\n  EVP_PKEY *evp_pkey() const { return evp_pkey_.get(); }\n\n private:\n  RTCCertificate(std::shared_ptr<X509> x509, std::shared_ptr<EVP_PKEY> evp_pkey);\n\n  std::shared_ptr<X509> x509_;\n  std::shared_ptr<EVP_PKEY> evp_pkey_;\n  std::string fingerprint_;\n};\n}\n"
  },
  {
    "path": "include/rtcdcpp/SCTPWrapper.hpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#pragma once\n\n/**\n * Wrapper around usrsctp.\n */\n\n#include \"ChunkQueue.hpp\"\n#include \"PeerConnection.hpp\"\n\n#include <thread>\n\n#include <usrsctp.h>\n\nnamespace rtcdcpp {\n\n#define MAX_OUT_STREAM 256\n#define MAX_IN_STREAM 256\n\nclass SCTPWrapper {\n public:\n  using MsgReceivedCallbackPtr = std::function<void(ChunkPtr chunk, uint16_t sid, uint32_t ppid)>;\n  using DTLSEncryptCallbackPtr = std::function<void(ChunkPtr)>;\n\n  SCTPWrapper(DTLSEncryptCallbackPtr dtlsEncryptCB, MsgReceivedCallbackPtr msgReceivedCB);\n  virtual ~SCTPWrapper();\n\n  bool Initialize();\n  void Start();\n  void Stop();\n  //  int GetStreamCursor();\n  //  void SetStreamCursor(int i);\n\n  // Handle a decrypted SCTP packet\n  void DTLSForSCTP(ChunkPtr chunk);\n\n  // Send a message to the remote connection\n  // Note, this will cause 1+ DTLSEncrypt callback calls\n  void GSForSCTP(ChunkPtr chunk, uint16_t sid, uint32_t ppid);\n\n private:\n  //  PeerConnection *peer_connection;\n  bool started{false};\n  struct socket *sock;\n  uint16_t local_port;\n  uint16_t remote_port;\n  int stream_cursor;\n\n  bool connectSentData{false};\n  std::mutex connectMtx;\n  std::condition_variable connectCV;\n\n  ChunkQueue send_queue;\n  ChunkQueue recv_queue;\n\n  const DTLSEncryptCallbackPtr dtlsEncryptCallback;\n  const MsgReceivedCallbackPtr msgReceivedCallback;\n\n  std::atomic<bool> should_stop{false};\n  std::thread recv_thread;\n  std::thread connect_thread;\n\n  void RunConnect();\n  void RecvLoop();\n\n  // SCTP has output a packet ready for DTLS\n  int OnSCTPForDTLS(void *data, size_t len, uint8_t tos, uint8_t set_df);\n\n  // SCTP has received a packet for GameSurge\n  int OnSCTPForGS(struct socket *sock, union sctp_sockstore addr, void *data, size_t len, struct sctp_rcvinfo recv_info, int flags);\n\n  void OnMsgReceived(const uint8_t *data, size_t len, int ppid, int sid);\n  void OnNotification(union sctp_notification *notify, size_t len);\n\n  // usrsctp callbacks\n  static int _OnSCTPForDTLS(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df);\n  static void _DebugLog(const char *format, ...);\n  static int _OnSCTPForGS(struct socket *sock, union sctp_sockstore addr, void *data, size_t len, struct sctp_rcvinfo recv_info, int flags,\n                          void *user_data);\n\n  std::shared_ptr<Logger> logger = GetLogger(\"rtcdcpp.SCTP\");\n};\n}\n"
  },
  {
    "path": "include/rtcdcpp/librtcdcpp.h",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * C Wrapper around the C++ Classes.\n */\n"
  },
  {
    "path": "src/DTLSWrapper.cpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Simple wrapper around OpenSSL DTLS.\n */\n\n#include \"rtcdcpp/DTLSWrapper.hpp\"\n#include \"rtcdcpp/RTCCertificate.hpp\"\n\n#include <iostream>\n\n#include <openssl/bio.h>\n#include <openssl/ec.h>\n#include <openssl/ssl.h>\n\nnamespace rtcdcpp {\n\nusing namespace std;\n\nDTLSWrapper::DTLSWrapper(PeerConnection *peer_connection)\n    : peer_connection(peer_connection), certificate_(nullptr), handshake_complete(false), should_stop(false) {\n  if (peer_connection->config().certificates.size() != 1) {\n    throw std::runtime_error(\"At least one and only one certificate has to be set\");\n  }\n  certificate_ = &peer_connection->config().certificates.front();\n  this->decrypted_callback = [](ChunkPtr x) { ; };\n  this->encrypted_callback = [](ChunkPtr x) { ; };\n}\n\nDTLSWrapper::~DTLSWrapper() {\n  Stop();\n\n  // NOTE: We intentionally do NOT free the BIO's manually\n\n  if (ssl) {\n    if (SSL_shutdown(ssl) == 0) {\n      SSL_shutdown(ssl);\n    }\n    SSL_free(ssl);\n    ssl = nullptr;\n  }\n  if (ctx) {\n    SSL_CTX_free(ctx);\n    ctx = nullptr;\n  }\n}\n\nstatic int verify_peer_certificate(int ok, X509_STORE_CTX *ctx) {\n  // XXX: This function should ask the user if they trust the cert\n  return 1;\n}\n\nbool DTLSWrapper::Initialize() {\n  SSL_library_init();\n  OpenSSL_add_all_algorithms();\n\n  ctx = SSL_CTX_new(DTLS_method());\n  if (!ctx) {\n    return false;\n  }\n\n  if (SSL_CTX_set_cipher_list(ctx, \"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\") != 1) {\n    return false;\n  }\n\n  SSL_CTX_set_read_ahead(ctx, 1);\n  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_peer_certificate);\n  SSL_CTX_use_PrivateKey(ctx, certificate_->evp_pkey());\n  SSL_CTX_use_certificate(ctx, certificate_->x509());\n\n  if (SSL_CTX_check_private_key(ctx) != 1) {\n    return false;\n  }\n\n  ssl = SSL_new(ctx);\n  if (!ssl) {\n    return false;\n  }\n\n  in_bio = BIO_new(BIO_s_mem());\n  if (!in_bio) {\n    return false;\n  }\n  BIO_set_mem_eof_return(in_bio, -1);\n\n  out_bio = BIO_new(BIO_s_mem());\n  if (!out_bio) {\n    return false;\n  }\n  BIO_set_mem_eof_return(out_bio, -1);\n\n  SSL_set_bio(ssl, in_bio, out_bio);\n\n  std::shared_ptr<EC_KEY> ecdh = std::shared_ptr<EC_KEY>(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1), EC_KEY_free);\n  SSL_set_options(ssl, SSL_OP_SINGLE_ECDH_USE);\n  SSL_set_tmp_ecdh(ssl, ecdh.get());\n\n  return true;\n}\n\nvoid DTLSWrapper::Start() {\n  SPDLOG_TRACE(logger, \"Start(): Starting handshake - {}\", std::this_thread::get_id());\n\n  // XXX: We can never be the server (sdp always returns active, not passive)\n  SSL_set_connect_state(ssl);\n  uint8_t buf[4192];\n  SSL_do_handshake(ssl);\n  while (BIO_ctrl_pending(out_bio) > 0) {\n    // XXX: This is not actually valid (buf + offset send after)\n    int nbytes = BIO_read(out_bio, buf, sizeof(buf));\n    if (nbytes > 0) {\n      SPDLOG_TRACE(logger, \"Start(): Sending handshake bytes {}\", nbytes);\n      this->encrypted_callback(std::make_shared<Chunk>(buf, nbytes));\n    }\n  }\n\n  // std::cerr << \"DTLS: handshake started, start encrypt/decrypt threads\" << std::endl;\n  this->encrypt_thread = std::thread(&DTLSWrapper::RunEncrypt, this);\n  this->decrypt_thread = std::thread(&DTLSWrapper::RunDecrypt, this);\n}\n\nvoid DTLSWrapper::Stop() {\n  this->should_stop = true;\n\n  encrypt_queue.Stop();\n  if (this->encrypt_thread.joinable()) {\n    this->encrypt_thread.join();\n  }\n\n  decrypt_queue.Stop();\n  if (this->decrypt_thread.joinable()) {\n    this->decrypt_thread.join();\n  }\n}\n\nvoid DTLSWrapper::SetEncryptedCallback(std::function<void(ChunkPtr chunk)> encrypted_callback) { this->encrypted_callback = encrypted_callback; }\n\nvoid DTLSWrapper::SetDecryptedCallback(std::function<void(ChunkPtr chunk)> decrypted_callback) { this->decrypted_callback = decrypted_callback; }\n\nvoid DTLSWrapper::DecryptData(ChunkPtr chunk) { this->decrypt_queue.push(chunk); }\n\nvoid DTLSWrapper::RunDecrypt() {\n  SPDLOG_TRACE(logger, \"RunDecrypt()\");\n\n  bool should_notify = false;\n  while (!should_stop) {\n    int read_bytes = 0;\n    uint8_t buf[2048] = {0};\n    ChunkPtr chunk = this->decrypt_queue.wait_and_pop();\n    if (!chunk) {\n      return;\n    }\n    size_t cur_len = chunk->Length();\n\n    {\n      std::lock_guard<std::mutex> lock(this->ssl_mutex);\n\n      // std::cout << \"DTLS: Decrypting data of size - \" << chunk->Length() << std::endl;\n      BIO_write(in_bio, chunk->Data(), (int)chunk->Length());\n      read_bytes = SSL_read(ssl, buf, sizeof(buf));\n\n      if (!handshake_complete) {\n        if (BIO_ctrl_pending(out_bio)) {\n          uint8_t out_buf[2048];\n          int send_bytes = 0;\n          while (BIO_ctrl_pending(out_bio) > 0) {\n            send_bytes += BIO_read(out_bio, out_buf + send_bytes, sizeof(out_buf) - send_bytes);\n          }\n          if (send_bytes > 0) {\n            this->encrypted_callback(std::make_shared<Chunk>(out_buf, send_bytes));\n          }\n        }\n\n        if (SSL_is_init_finished(ssl)) {\n          handshake_complete = true;\n          should_notify = true;\n        }\n      }\n    }\n\n    // std::cerr << \"Read this many bytes \" << read_bytes << std::endl;\n    if (read_bytes > 0) {\n      // std::cerr << \"DTLS: Calling decrypted callback with data of size: \" << read_bytes << std::endl;\n      this->decrypted_callback(std::make_shared<Chunk>(buf, read_bytes));\n    } else {\n      // TODO: SSL error checking\n    }\n\n    if (should_notify) {\n      // std::cerr << \"DTLS: handshake is done\" << std::endl;\n      should_notify = false;\n      peer_connection->OnDTLSHandshakeDone();\n    }\n  }\n}\n\nvoid DTLSWrapper::EncryptData(ChunkPtr chunk) { this->encrypt_queue.push(chunk); }\n\nvoid DTLSWrapper::RunEncrypt() {\n  SPDLOG_TRACE(logger, \"RunEncrypt()\");\n  while (!this->should_stop) {\n    ChunkPtr chunk = this->encrypt_queue.wait_and_pop();\n    if (!chunk) {\n      return;\n    }\n\n    // std::cerr << \"DTLS: Encrypting message of len - \" << chunk->Length() << std::endl;\n    {\n      std::lock_guard<std::mutex> lock(this->ssl_mutex);\n      uint8_t buf[2048] = {0};\n      if (SSL_write(ssl, chunk->Data(), (int)chunk->Length()) != chunk->Length()) {\n        // TODO: Error handling\n      }\n\n      int nbytes = 0;\n      while (BIO_ctrl_pending(out_bio) > 0) {\n        nbytes += BIO_read(out_bio, buf + nbytes, 2048 - nbytes);\n      }\n\n      if (nbytes > 0) {\n        // std::cerr << \"DTLS: Calling the encrypted data cb\" << std::endl;\n        this->encrypted_callback(std::make_shared<Chunk>(buf, nbytes));\n      }\n    }\n  }\n}\n}\n"
  },
  {
    "path": "src/DataChannel.cpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * DataChannel.\n */\n\n#include <iostream>\n\n#include \"rtcdcpp/DataChannel.hpp\"\n#include \"rtcdcpp/PeerConnection.hpp\"\n\nnamespace rtcdcpp {\n\nDataChannel::DataChannel(PeerConnection *pc, uint16_t stream_id, uint8_t chan_type, std::string label, std::string protocol)\n    : pc(pc), stream_id(stream_id), chan_type(chan_type), label(label), protocol(protocol) {\n  // XXX: Default-noop callbacks\n  open_cb = []() { ; };  // XXX: I love and hate that this is valid c++\n  str_msg_cb = [](std::string x) { ; };\n  bin_msg_cb = [](ChunkPtr data) { ; };\n  closed_cb = []() { ; };\n  error_cb = [](std::string x) { ; };\n}\n\nDataChannel::~DataChannel() { ; }\n\nuint16_t DataChannel::GetStreamID() { return this->stream_id; }\n\nuint8_t DataChannel::GetChannelType() { return this->chan_type; }\n\nstd::string DataChannel::GetLabel() { return this->label; }\n\nstd::string DataChannel::GetProtocol() { return this->protocol; }\n\n/**\n * TODO: Close the DataChannel.\n */\nvoid Close() { ; }\n\nbool DataChannel::SendString(std::string msg) {\n  std::cerr << \"DC: Sending string: \" << msg << std::endl;\n  this->pc->SendStrMsg(msg, this->stream_id);\n  return true;\n}\n\n// TODO Take a shared_ptr to datachunk\nbool DataChannel::SendBinary(const uint8_t *msg, int len) {\n  std::cerr << \"DC: Sending binary of len - \" << len << std::endl;\n  this->pc->SendBinaryMsg(msg, len, this->stream_id);\n  std::cerr << \"DC: Binary sent\" << std::endl;\n  return true;\n}\n\nvoid DataChannel::SetOnOpen(std::function<void()> open_cb) { this->open_cb = open_cb; }\n\nvoid DataChannel::SetOnStringMsgCallback(std::function<void(std::string msg)> str_msg_cb) { this->str_msg_cb = str_msg_cb; }\n\nvoid DataChannel::SetOnBinaryMsgCallback(std::function<void(ChunkPtr)> bin_msg_cb) { this->bin_msg_cb = bin_msg_cb; }\n\nvoid DataChannel::SetOnClosedCallback(std::function<void()> closed_cb) { this->closed_cb = closed_cb; }\n\nvoid DataChannel::SetOnErrorCallback(std::function<void(std::string description)> error_cb) { this->error_cb = error_cb; }\n\nvoid DataChannel::OnOpen() {\n  if (this->open_cb) {\n    this->open_cb();\n  }\n}\n\nvoid DataChannel::OnStringMsg(std::string msg) {\n  if (this->str_msg_cb) {\n    this->str_msg_cb(msg);\n  }\n}\n\nvoid DataChannel::OnBinaryMsg(ChunkPtr msg) {\n  if (this->bin_msg_cb) {\n    this->bin_msg_cb(msg);\n  }\n}\n\nvoid DataChannel::OnClosed() {\n  if (this->closed_cb) {\n    this->closed_cb();\n  }\n}\n\nvoid DataChannel::OnError(std::string description) {\n  if (this->error_cb) {\n    this->error_cb(description);\n  }\n}\n}\n"
  },
  {
    "path": "src/Logging.cpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"rtcdcpp/Logging.hpp\"\n\nnamespace rtcdcpp {\n\n#ifndef SPDLOG_DISABLED\n\n#include <mutex>\n\nstd::shared_ptr<Logger> GetLogger(const std::string &logger_name) {\n  auto logger = spdlog::get(logger_name);\n  //spdlog::set_level(spdlog::level::trace); // Set global log level to debug\n\n  if (logger) {\n    return logger;\n  }\n  return spdlog::stdout_color_mt(logger_name);\n}\n\n#else\n\nstd::shared_ptr<Logger> GetLogger(const std::string &logger) {\n  return std::make_shared<Logger>();\n}\n\n#endif\n\n}\n"
  },
  {
    "path": "src/NiceWrapper.cpp",
    "content": "/**\r\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are met:\r\n *    * Redistributions of source code must retain the above copyright\r\n *      notice, this list of conditions and the following disclaimer.\r\n *    * Redistributions in binary form must reproduce the above copyright\r\n *      notice, this list of conditions and the following disclaimer in the\r\n *      documentation and/or other materials provided with the distribution.\r\n *    * Neither the name of the <organization> nor the\r\n *      names of its contributors may be used to endorse or promote products\r\n *      derived from this software without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\r\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\r\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n */\r\n\r\n/**\r\n * Basic implementation of libnice stuff.\r\n */\r\n\r\n#include \"rtcdcpp/NiceWrapper.hpp\"\r\n\r\n#include <sstream>\r\n\r\n#include <netdb.h>\r\n\r\nvoid ReplaceAll(std::string &s, const std::string &search, const std::string &replace) {\r\n  size_t pos = 0;\r\n  while ((pos = s.find(search, pos)) != std::string::npos) {\r\n    s.replace(pos, search.length(), replace);\r\n    pos += replace.length();\r\n  }\r\n}\r\n\r\nnamespace rtcdcpp {\r\n\r\nusing namespace std;\r\n\r\nNiceWrapper::NiceWrapper(PeerConnection *peer_connection)\r\n    : peer_connection(peer_connection), stream_id(0), should_stop(false), send_queue(), agent(NULL, nullptr), loop(NULL, nullptr), packets_sent(0) {\r\n  data_received_callback = [](ChunkPtr x) { ; };\r\n  nice_debug_disable(true);\r\n}\r\n\r\nNiceWrapper::~NiceWrapper() { Stop(); }\r\n\r\nvoid new_local_candidate(NiceAgent *agent, NiceCandidate *candidate, gpointer user_data) {\r\n  NiceWrapper *nice = (NiceWrapper *)user_data;\r\n  gchar *cand = nice_agent_generate_local_candidate_sdp(agent, candidate);\r\n  std::string cand_str(cand);\r\n  nice->OnCandidate(cand_str);\r\n  g_free(cand);\r\n}\r\n\r\nvoid NiceWrapper::OnCandidate(std::string candidate) {\r\n  SPDLOG_DEBUG(logger, \"On candidate: {}\", candidate);\r\n  this->peer_connection->OnLocalIceCandidate(candidate);\r\n}\r\n\r\nvoid candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer user_data) {\r\n  NiceWrapper *nice = (NiceWrapper *)user_data;\r\n  nice->OnGatheringDone();\r\n}\r\n\r\n// TODO: Callback for this\r\nvoid NiceWrapper::OnGatheringDone() {\r\n  SPDLOG_DEBUG(logger, \"ICE: candidate gathering done\");\r\n  std::string empty_candidate(\"\");\r\n  this->peer_connection->OnLocalIceCandidate(empty_candidate);\r\n}\r\n\r\n// TODO: Callbacks on failure\r\nvoid component_state_changed(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer user_data) {\r\n  NiceWrapper *nice = (NiceWrapper *)user_data;\r\n  nice->OnStateChange(stream_id, component_id, state);\r\n}\r\n\r\nvoid NiceWrapper::OnStateChange(uint32_t stream_id, uint32_t component_id, uint32_t state) {\r\n  switch (state) {\r\n    case (NICE_COMPONENT_STATE_DISCONNECTED):\r\n      SPDLOG_TRACE(logger, \"ICE: DISCONNECTED\");\r\n      break;\r\n    case (NICE_COMPONENT_STATE_GATHERING):\r\n      SPDLOG_TRACE(logger, \"ICE: GATHERING\");\r\n      break;\r\n    case (NICE_COMPONENT_STATE_CONNECTING):\r\n      SPDLOG_TRACE(logger, \"ICE: CONNECTING\");\r\n      break;\r\n    case (NICE_COMPONENT_STATE_CONNECTED):\r\n      SPDLOG_TRACE(logger, \"ICE: CONNECTED\");\r\n      break;\r\n    case (NICE_COMPONENT_STATE_READY):\r\n      SPDLOG_TRACE(logger, \"ICE: READY\");\r\n      this->OnIceReady();\r\n      break;\r\n    case (NICE_COMPONENT_STATE_FAILED):\r\n      SPDLOG_TRACE(logger, \"ICE FAILED: stream_id={} - component_id={}\", stream_id, component_id);\r\n      break;\r\n    default:\r\n      SPDLOG_TRACE(logger, \"ICE: Unknown state: {}\", state);\r\n      break;\r\n  }\r\n}\r\n\r\n// TODO: Turn this into a callback\r\nvoid NiceWrapper::OnIceReady() { this->peer_connection->OnIceReady(); }\r\n\r\nvoid new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *lcandidate, NiceCandidate *rcandidate,\r\n                       gpointer user_data) {\r\n  GetLogger(\"librtcpp.Nice\")->error(\"ICE: new selected pair\");\r\n  NiceWrapper *nice = (NiceWrapper *)user_data;\r\n  nice->OnSelectedPair();\r\n}\r\n\r\nvoid NiceWrapper::OnSelectedPair() { SPDLOG_TRACE(logger, \"OnSelectedPair\"); }\r\n\r\nvoid data_received(NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data) {\r\n  NiceWrapper *nice = (NiceWrapper *)user_data;\r\n  nice->OnDataReceived((const uint8_t *)buf, len);\r\n}\r\n\r\nvoid NiceWrapper::OnDataReceived(const uint8_t *buf, int len) {\r\n  SPDLOG_TRACE(logger, \"Nice data IN: {}\", len);\r\n  this->data_received_callback(std::make_shared<Chunk>(buf, len));\r\n}\r\n\r\nvoid nice_log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) {\r\n  NiceWrapper *nice = (NiceWrapper *)user_data;\r\n  nice->LogMessage(message);\r\n}\r\n\r\nvoid NiceWrapper::LogMessage(const gchar *message) { SPDLOG_TRACE(logger, \"libnice: {}\", message); }\r\n\r\nbool NiceWrapper::Initialize() {\r\n  auto config = peer_connection->config();\r\n\r\n  int log_flags = G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION;\r\n  g_log_set_handler(NULL, (GLogLevelFlags)log_flags, nice_log_handler, this);\r\n  this->loop = std::unique_ptr<GMainLoop, void (*)(GMainLoop *)>(g_main_loop_new(NULL, FALSE), g_main_loop_unref);\r\n  if (!this->loop) {\r\n    SPDLOG_TRACE(logger, \"Failed to initialize GMainLoop\");\r\n  }\r\n\r\n  this->agent = std::unique_ptr<NiceAgent, decltype(&g_object_unref)>(nice_agent_new(g_main_loop_get_context(loop.get()), NICE_COMPATIBILITY_RFC5245),\r\n                                                                      g_object_unref);\r\n  if (!this->agent) {\r\n    SPDLOG_TRACE(logger, \"Failed to initialize nice agent\");\r\n    return false;\r\n  }\r\n\r\n  this->g_main_loop_thread = std::thread(g_main_loop_run, this->loop.get());\r\n\r\n  g_object_set(G_OBJECT(agent.get()), \"upnp\", FALSE, NULL);\r\n  g_object_set(G_OBJECT(agent.get()), \"controlling-mode\", 0, NULL);\r\n\r\n  if (config.ice_servers.size() > 1) {\r\n    throw std::invalid_argument(\"Only up to one ICE server is currently supported\");\r\n  }\r\n\r\n  for (auto ice_server : config.ice_servers) {\r\n    struct hostent *stun_host = gethostbyname(ice_server.hostname.c_str());\r\n    if (stun_host == nullptr) {\r\n      logger->warn(\"Failed to lookup host for server: {}\", ice_server);\r\n    } else {\r\n      in_addr *address = (in_addr *)stun_host->h_addr;\r\n      const char *ip_address = inet_ntoa(*address);\r\n\r\n      g_object_set(G_OBJECT(agent.get()), \"stun-server\", ip_address, NULL);\r\n    }\r\n\r\n    if (ice_server.port > 0) {\r\n      g_object_set(G_OBJECT(agent.get()), \"stun-server-port\", ice_server.port, NULL);\r\n    } else {\r\n      logger->error(\"stun port empty\");\r\n    }\r\n  }\r\n\r\n  g_signal_connect(G_OBJECT(agent.get()), \"candidate-gathering-done\", G_CALLBACK(candidate_gathering_done), this);\r\n  g_signal_connect(G_OBJECT(agent.get()), \"component-state-changed\", G_CALLBACK(component_state_changed), this);\r\n  g_signal_connect(G_OBJECT(agent.get()), \"new-candidate-full\", G_CALLBACK(new_local_candidate), this);\r\n  g_signal_connect(G_OBJECT(agent.get()), \"new-selected-pair\", G_CALLBACK(new_selected_pair), this);\r\n\r\n  // TODO: Learn more about nice streams\r\n  this->stream_id = nice_agent_add_stream(agent.get(), 1);\r\n  if (this->stream_id == 0) {\r\n    return false;\r\n  }\r\n\r\n  nice_agent_set_stream_name(agent.get(), this->stream_id, \"application\");\r\n\r\n  if (!config.ice_ufrag.empty() && !config.ice_pwd.empty()) {\r\n    nice_agent_set_local_credentials(agent.get(), this->stream_id, config.ice_ufrag.c_str(), config.ice_pwd.c_str());\r\n  }\r\n\r\n  if (config.ice_port_range.first != 0 || config.ice_port_range.second != 0) {\r\n    nice_agent_set_port_range(agent.get(), this->stream_id, 1, config.ice_port_range.first, config.ice_port_range.second);\r\n  }\r\n\r\n  return (bool)nice_agent_attach_recv(agent.get(), this->stream_id, 1, g_main_loop_get_context(loop.get()), data_received, this);\r\n}\r\n\r\nvoid NiceWrapper::StartSendLoop() { this->send_thread = std::thread(&NiceWrapper::SendLoop, this); }\r\n\r\nvoid NiceWrapper::Stop() {\r\n  this->should_stop = true;\r\n\r\n  send_queue.Stop();\r\n  if (this->send_thread.joinable()) {\r\n    this->send_thread.join();\r\n  }\r\n\r\n  g_main_loop_quit(this->loop.get());\r\n\r\n  if (this->g_main_loop_thread.joinable()) {\r\n    this->g_main_loop_thread.join();\r\n  }\r\n}\r\n\r\nvoid NiceWrapper::ParseRemoteSDP(std::string remote_sdp) {\r\n  string crfree_remote_sdp = remote_sdp;\r\n\r\n  // TODO: Improve this. This is needed because otherwise libnice will wrongly take the '\\r' as part of ice-ufrag/password.\r\n  ReplaceAll(crfree_remote_sdp, \"\\r\\n\", \"\\n\");\r\n\r\n  int rc = nice_agent_parse_remote_sdp(this->agent.get(), crfree_remote_sdp.c_str());\r\n\r\n  if (rc < 0) {\r\n    throw std::runtime_error(\"ParseRemoteSDP: \" + std::string(strerror(rc)));\r\n  } else {\r\n    logger->info(\"ICE: Added {} Candidates\", rc);\r\n  }\r\n\r\n  if (!nice_agent_gather_candidates(agent.get(), this->stream_id)) {\r\n    throw std::runtime_error(\"ParseRemoteSDP: Error gathering candidates!\");\r\n  }\r\n}\r\n\r\nvoid NiceWrapper::SendData(ChunkPtr chunk) {\r\n  if (this->stream_id == 0) {\r\n    SPDLOG_TRACE(logger, \"ICE: ERROR sending data to unitialized nice context\");\r\n    return;\r\n  }\r\n\r\n  this->send_queue.push(chunk);\r\n}\r\n\r\n// Pull items off the send queue and call nice_agent_send\r\nvoid NiceWrapper::SendLoop() {\r\n  while (!this->should_stop) {\r\n    ChunkPtr chunk = send_queue.wait_and_pop();\r\n    if (!chunk) {\r\n      return;\r\n    }\r\n    size_t cur_len = chunk->Length();\r\n    int result = 0;\r\n    // std::cerr << \"ICE: Sending data of len \" << cur_len << std::endl;\r\n    SPDLOG_TRACE(logger, \"Nice data OUT: {}\", cur_len);\r\n    result = nice_agent_send(this->agent.get(), this->stream_id, 1, (guint)cur_len, (const char *)chunk->Data());\r\n    if (result != cur_len) {\r\n      SPDLOG_TRACE(logger, \"ICE: Failed to send data of len - {}\", cur_len);\r\n      SPDLOG_TRACE(logger, \"ICE: Failed send result - {}\", result);\r\n    } else {\r\n      // std::cerr << \"ICE: Data sent \" << cur_len << std::endl;\r\n    }\r\n  }\r\n}\r\n\r\nstd::string NiceWrapper::GenerateLocalSDP() {\r\n  std::stringstream nice_sdp;\r\n  std::stringstream result;\r\n  std::string line;\r\n\r\n  gchar *raw_sdp = nice_agent_generate_local_sdp(agent.get());\r\n  nice_sdp << raw_sdp;\r\n\r\n  while (std::getline(nice_sdp, line)) {\r\n    if (g_str_has_prefix(line.c_str(), \"a=ice-ufrag:\") || g_str_has_prefix(line.c_str(), \"a=ice-pwd:\")) {\r\n      result << line << \"\\r\\n\";\r\n    }\r\n  }\r\n  g_free(raw_sdp);\r\n  return result.str();\r\n}\r\n\r\nbool NiceWrapper::SetRemoteIceCandidate(string candidate_sdp) {\r\n  GSList *list = NULL;\r\n  NiceCandidate *rcand = nice_agent_parse_remote_candidate_sdp(this->agent.get(), this->stream_id, candidate_sdp.c_str());\r\n\r\n  if (rcand == NULL) {\r\n    SPDLOG_TRACE(logger, \"failed to parse remote candidate\");\r\n    return false;\r\n  }\r\n  list = g_slist_append(list, rcand);\r\n\r\n  bool success = (nice_agent_set_remote_candidates(this->agent.get(), this->stream_id, 1, list) > 0);\r\n\r\n  g_slist_free_full(list, (GDestroyNotify)&nice_candidate_free);\r\n\r\n  return success;\r\n}\r\n\r\nbool NiceWrapper::SetRemoteIceCandidates(vector<string> candidate_sdps) {\r\n  GSList *list = NULL;\r\n  for (auto candidate_sdp : candidate_sdps) {\r\n    NiceCandidate *rcand = nice_agent_parse_remote_candidate_sdp(this->agent.get(), this->stream_id, candidate_sdp.c_str());\r\n\r\n    if (rcand == NULL) {\r\n      SPDLOG_TRACE(logger, \"failed to parse remote candidate\");\r\n      return false;\r\n    }\r\n    list = g_slist_append(list, rcand);\r\n  }\r\n\r\n  bool success = (nice_agent_set_remote_candidates(this->agent.get(), this->stream_id, 1, list) > 0);\r\n\r\n  g_slist_free_full(list, (GDestroyNotify)&nice_candidate_free);\r\n\r\n  return success;\r\n}\r\n\r\nvoid NiceWrapper::SetDataReceivedCallback(std::function<void(ChunkPtr)> data_received_callback) {\r\n  this->data_received_callback = data_received_callback;\r\n}\r\n}\r\n"
  },
  {
    "path": "src/PeerConnection.cpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * RTC Handler.\n */\n\n#include \"rtcdcpp/PeerConnection.hpp\"\n#include \"rtcdcpp/DTLSWrapper.hpp\"\n#include \"rtcdcpp/NiceWrapper.hpp\"\n#include \"rtcdcpp/SCTPWrapper.hpp\"\n\n#include <sstream>\n\n#define SESSION_ID_SIZE 16\n\nnamespace rtcdcpp {\n\nusing namespace std;\n\nstd::ostream &operator<<(std::ostream &os, const RTCIceServer &ice_server) { return os << ice_server.hostname << \":\" << ice_server.port; }\n\nPeerConnection::PeerConnection(const RTCConfiguration &config, IceCandidateCallbackPtr icCB, DataChannelCallbackPtr dcCB)\n    : config_(config), ice_candidate_cb(icCB), new_channel_cb(dcCB) {\n  if (config_.certificates.empty()) {\n    config_.certificates.push_back(RTCCertificate::GenerateCertificate(\"rtcdcpp\", 365));\n  }\n  if (!Initialize()) {\n    throw runtime_error(\"Could not initialize\");\n  }\n}\n\nPeerConnection::~PeerConnection() {\n  sctp->Stop();\n  dtls->Stop();\n  nice->Stop();\n}\n\nbool PeerConnection::Initialize() {\n  this->nice = make_unique<NiceWrapper>(this);\n  this->dtls = make_unique<DTLSWrapper>(this);\n  this->sctp = make_unique<SCTPWrapper>(\n      std::bind(&DTLSWrapper::EncryptData, dtls.get(), std::placeholders::_1),\n      std::bind(&PeerConnection::OnSCTPMsgReceived, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));\n\n  if (!dtls->Initialize()) {\n    logger->error(\"DTLS failure\");\n    return false;\n  }\n  SPDLOG_DEBUG(logger, \"RTC: dtls initialized\");\n\n  if (!nice->Initialize()) {\n    logger->error(\"Nice failure\");\n    return false;\n  }\n  SPDLOG_DEBUG(logger, \"RTC: nice initialized\");\n\n  if (!sctp->Initialize()) {\n    logger->error(\"sctp failure\");\n    return false;\n  }\n  SPDLOG_DEBUG(logger, \"RTC: sctp initialized\");\n\n  nice->SetDataReceivedCallback(std::bind(&DTLSWrapper::DecryptData, dtls.get(), std::placeholders::_1));\n  dtls->SetDecryptedCallback(std::bind(&SCTPWrapper::DTLSForSCTP, sctp.get(), std::placeholders::_1));\n  dtls->SetEncryptedCallback(std::bind(&NiceWrapper::SendData, nice.get(), std::placeholders::_1));\n  nice->StartSendLoop();\n  return true;\n}\n\nvoid PeerConnection::ParseOffer(std::string offer_sdp) {\n  std::stringstream ss(offer_sdp);\n  std::string line;\n\n  while (std::getline(ss, line)) {\n    if (g_str_has_prefix(line.c_str(), \"a=setup:\")) {\n      std::size_t pos = line.find(\":\") + 1;\n      std::string setup = line.substr(pos);\n      if (setup == \"active\" && this->role == Client) {\n        this->role = Server;\n      } else if (setup == \"passive\" && this->role == Server) {\n        this->role = Client;\n      } else {  // actpass\n        // nothing to do\n      }\n    } else if (g_str_has_prefix(line.c_str(), \"a=mid:\")) {\n      std::size_t pos = line.find(\":\") + 1;\n      std::size_t end = line.find(\"\\r\");\n      this->mid = line.substr(pos, end - pos);\n    }\n  }\n  nice->ParseRemoteSDP(offer_sdp);\n}\n\nstd::string random_session_id() {\n  const static char *numbers = \"0123456789\";\n  srand((unsigned)time(nullptr));\n  std::stringstream result;\n\n  for (int i = 0; i < SESSION_ID_SIZE; ++i) {\n    int r = rand() % 10;\n    result << numbers[r];\n  }\n  return result.str();\n}\n\nstd::string PeerConnection::GenerateAnswer() {\n  std::stringstream sdp;\n  std::string session_id = random_session_id();\n  SPDLOG_TRACE(logger, \"Generating Answer SDP: session_id={}\", session_id);\n\n  sdp << \"v=0\\r\\n\";\n  sdp << \"o=- \" << session_id << \" 2 IN IP4 0.0.0.0\\r\\n\";  // Session ID\n  sdp << \"s=-\\r\\n\";\n  sdp << \"t=0 0\\r\\n\";\n  sdp << \"a=msid-semantic: WMS\\r\\n\";\n  sdp << \"m=application 9 DTLS/SCTP 5000\\r\\n\";  // XXX: hardcoded port\n  sdp << \"c=IN IP4 0.0.0.0\\r\\n\";\n  sdp << this->nice->GenerateLocalSDP();\n  sdp << \"a=fingerprint:sha-256 \" << dtls->certificate()->fingerprint() << \"\\r\\n\";\n  sdp << \"a=ice-options:trickle\\r\\n\";\n  sdp << \"a=setup:\" << (this->role == Client ? \"active\" : \"passive\") << \"\\r\\n\";\n  sdp << \"a=mid:\" << this->mid << \"\\r\\n\";\n  sdp << \"a=sctpmap:5000 webrtc-datachannel 1024\\r\\n\";\n\n  return sdp.str();\n}\n\nbool PeerConnection::SetRemoteIceCandidate(string candidate_sdp) { return this->nice->SetRemoteIceCandidate(candidate_sdp); }\n\nbool PeerConnection::SetRemoteIceCandidates(vector<string> candidate_sdps) { return this->nice->SetRemoteIceCandidates(candidate_sdps); }\n\nvoid PeerConnection::OnLocalIceCandidate(std::string &ice_candidate) {\n  if (this->ice_candidate_cb) {\n    if (ice_candidate.size() > 2) {\n      ice_candidate = ice_candidate.substr(2);\n    }\n    IceCandidate candidate(ice_candidate, this->mid, 0);\n    this->ice_candidate_cb(candidate);\n  }\n}\n\nvoid PeerConnection::OnIceReady() {\n  SPDLOG_TRACE(logger, \"OnIceReady(): Time to ping DTLS\");\n  if (!iceReady) {\n    iceReady = true;\n    this->dtls->Start();\n  } else {\n    // TODO work out\n    logger->warn(\"OnIceReady(): Called twice!!\");\n  }\n}\n\nvoid PeerConnection::OnDTLSHandshakeDone() {\n  SPDLOG_TRACE(logger, \"OnDTLSHandshakeDone(): Time to get the SCTP party started\");\n  this->sctp->Start();\n}\n\n// Matches DataChannel onmessage\nvoid PeerConnection::OnSCTPMsgReceived(ChunkPtr chunk, uint16_t sid, uint32_t ppid) {\n  SPDLOG_TRACE(logger, \"OnSCTPMsgReceived(): Handling an sctp message\");\n  if (ppid == PPID_CONTROL) {\n    SPDLOG_TRACE(logger, \"Control PPID\");\n    if (chunk->Data()[0] == DC_TYPE_OPEN) {\n      SPDLOG_TRACE(logger, \"New channel time!\");\n      HandleNewDataChannel(chunk, sid);\n    } else if (chunk->Data()[0] == DC_TYPE_ACK) {\n      SPDLOG_TRACE(logger, \"DC ACK\");\n      // HandleDataChannelAck(chunk, sid); XXX: Don't care right now\n    } else {\n      SPDLOG_TRACE(logger, \"Unknown msg_type for ppid control: {}\", chunk->Data()[0]);\n    }\n  } else if ((ppid == PPID_STRING) || (ppid == PPID_STRING_EMPTY)) {\n    SPDLOG_TRACE(logger, \"String msg\");\n    HandleStringMessage(chunk, sid);\n  } else if ((ppid == PPID_BINARY) || (ppid == PPID_BINARY_EMPTY)) {\n    SPDLOG_TRACE(logger, \"Binary msg\");\n    HandleBinaryMessage(chunk, sid);\n  } else {\n    logger->error(\"Unknown ppid={}\", ppid);\n  }\n}\n\nstd::shared_ptr<DataChannel> PeerConnection::GetChannel(uint16_t sid) {\n  auto iter = data_channels.find(sid);\n  if (iter != data_channels.end()) {\n    return data_channels[sid];\n  }\n\n  return std::shared_ptr<DataChannel>();\n}\n\nvoid PeerConnection::HandleNewDataChannel(ChunkPtr chunk, uint16_t sid) {\n  uint8_t *raw_msg = chunk->Data();\n  dc_open_msg open_msg;\n  open_msg.chan_type = raw_msg[1];\n  open_msg.priority = (raw_msg[2] << 8) + raw_msg[3];\n  open_msg.reliability = (raw_msg[4] << 24) + (raw_msg[5] << 16) + (raw_msg[6] << 8) + raw_msg[7];\n  open_msg.label_len = (raw_msg[8] << 8) + raw_msg[9];\n  open_msg.protocol_len = (raw_msg[10] << 8) + raw_msg[11];\n\n  std::string label(reinterpret_cast<char *>(raw_msg + 12), open_msg.label_len);\n  std::string protocol(reinterpret_cast<char *>(raw_msg + 12 + open_msg.label_len), open_msg.protocol_len);\n\n  SPDLOG_DEBUG(logger, \"Creating channel with sid: {}, chan_type: {}, label: {}, protocol: {}\", sid, open_msg.chan_type, label, protocol);\n\n  // TODO: Support overriding an existing channel\n  auto new_channel = std::make_shared<DataChannel>(this, sid, open_msg.chan_type, label, protocol);\n\n  data_channels[sid] = new_channel;\n\n  if (this->new_channel_cb) {\n    this->new_channel_cb(new_channel);\n  } else {\n    logger->warn(\"No new channel callback, ignoring new channel\");\n  }\n}\n\nvoid PeerConnection::HandleStringMessage(ChunkPtr chunk, uint16_t sid) {\n  auto cur_channel = GetChannel(sid);\n  if (!cur_channel) {\n    logger->warn(\"Received msg on unknown channel: {}\", sid);\n    return;\n  }\n\n  std::string cur_msg(reinterpret_cast<char *>(chunk->Data()), chunk->Length());\n\n  cur_channel->OnStringMsg(cur_msg);\n}\n\nvoid PeerConnection::HandleBinaryMessage(ChunkPtr chunk, uint16_t sid) {\n  auto cur_channel = GetChannel(sid);\n  if (!cur_channel) {\n    logger->warn(\"Received binary msg on unknown channel: {}\", sid);\n    return;\n  }\n\n  cur_channel->OnBinaryMsg(chunk);\n}\n\nvoid PeerConnection::SendStrMsg(std::string str_msg, uint16_t sid) {\n  auto cur_msg = std::make_shared<Chunk>((const uint8_t *)str_msg.c_str(), str_msg.size());\n  this->sctp->GSForSCTP(cur_msg, sid, PPID_STRING);\n}\n\nvoid PeerConnection::SendBinaryMsg(const uint8_t *data, int len, uint16_t sid) {\n  auto cur_msg = std::make_shared<Chunk>(data, len);\n  this->sctp->GSForSCTP(cur_msg, sid, PPID_BINARY);\n}\n}\n"
  },
  {
    "path": "src/RTCCertificate.cpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Simple wrapper around OpenSSL Certs.\n */\n\n#include \"rtcdcpp/RTCCertificate.hpp\"\n\n#include <openssl/pem.h>\n\nnamespace rtcdcpp {\n\nusing namespace std;\n\nstatic std::shared_ptr<X509> GenerateX509(std::shared_ptr<EVP_PKEY> evp_pkey, const std::string &common_name, int days) {\n  std::shared_ptr<X509> null_result;\n\n  std::shared_ptr<X509> x509(X509_new(), X509_free);\n  std::shared_ptr<BIGNUM> serial_number(BN_new(), BN_free);\n  std::shared_ptr<X509_NAME> name(X509_NAME_new(), X509_NAME_free);\n\n  if (!x509 || !serial_number || !name) {\n    return null_result;\n  }\n\n  if (!X509_set_pubkey(x509.get(), evp_pkey.get())) {\n    return null_result;\n  }\n\n  if (!BN_pseudo_rand(serial_number.get(), 64, 0, 0)) {\n    return null_result;\n  }\n\n  ASN1_INTEGER *asn1_serial_number = X509_get_serialNumber(x509.get());\n  if (!asn1_serial_number) {\n    return null_result;\n  }\n\n  if (!BN_to_ASN1_INTEGER(serial_number.get(), asn1_serial_number)) {\n    return null_result;\n  }\n\n  if (!X509_set_version(x509.get(), 0L)) {\n    return null_result;\n  }\n\n  if (!X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_UTF8, (unsigned char *)common_name.c_str(), -1, -1, 0)) {\n    return null_result;\n  }\n\n  if (!X509_set_subject_name(x509.get(), name.get()) || !X509_set_issuer_name(x509.get(), name.get())) {\n    return null_result;\n  }\n\n  if (!X509_gmtime_adj(X509_get_notBefore(x509.get()), 0) || !X509_gmtime_adj(X509_get_notAfter(x509.get()), days * 24 * 3600)) {\n    return null_result;\n  }\n\n  if (!X509_sign(x509.get(), evp_pkey.get(), EVP_sha1())) {\n    return null_result;\n  }\n\n  return x509;\n}\n\nstatic std::string GenerateFingerprint(std::shared_ptr<X509> x509) {\n  unsigned int len;\n  unsigned char buf[4096] = {0};\n  if (!X509_digest(x509.get(), EVP_sha256(), buf, &len)) {\n    throw std::runtime_error(\"GenerateFingerprint(): X509_digest error\");\n  }\n\n  if (len > SHA256_FINGERPRINT_SIZE) {\n    throw std::runtime_error(\"GenerateFingerprint(): fingerprint size too large for buffer!\");\n  }\n\n  int offset = 0;\n  char fp[SHA256_FINGERPRINT_SIZE];\n  memset(fp, 0, SHA256_FINGERPRINT_SIZE);\n  for (unsigned int i = 0; i < len; ++i) {\n    snprintf(fp + offset, 4, \"%02X:\", buf[i]);\n    offset += 3;\n  }\n  fp[offset - 1] = '\\0';\n  return std::string(fp);\n}\n\nRTCCertificate RTCCertificate::GenerateCertificate(std::string common_name, int days) {\n  std::shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);\n  RSA *rsa = RSA_new();\n\n  std::shared_ptr<BIGNUM> exponent(BN_new(), BN_free);\n\n  if (!pkey || !rsa || !exponent) {\n    throw std::runtime_error(\"GenerateCertificate: !pkey || !rsa || !exponent\");\n  }\n\n  if (!BN_set_word(exponent.get(), RSA_F4) \n      || !RSA_generate_key_ex(rsa, 2048, exponent.get(), NULL)\n      || !EVP_PKEY_assign_RSA(pkey.get(), rsa)\n  ){\n    throw std::runtime_error(\"GenerateCertificate: Error generating key\");\n  }\n  auto cert = GenerateX509(pkey, common_name, days);\n\n  if (!cert) {\n    throw std::runtime_error(\"GenerateCertificate: Error in GenerateX509\");\n  }\n  return RTCCertificate(cert, pkey);\n}\n\nRTCCertificate::RTCCertificate(std::string cert_pem, std::string pkey_pem) {\n  /* x509 */\n  BIO *bio = BIO_new(BIO_s_mem());\n  BIO_write(bio, cert_pem.c_str(), (int)cert_pem.length());\n\n  x509_ = std::shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, 0, 0), X509_free);\n  BIO_free(bio);\n  if (!x509_) {\n    throw std::invalid_argument(\"Could not read cert_pem\");\n  }\n\n  /* evp_pkey */\n  bio = BIO_new(BIO_s_mem());\n  BIO_write(bio, pkey_pem.c_str(), (int)pkey_pem.length());\n\n  evp_pkey_ = std::shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, 0, 0), EVP_PKEY_free);\n  BIO_free(bio);\n\n  if (!evp_pkey_) {\n    throw std::invalid_argument(\"Could not read pkey_pem\");\n  }\n\n  fingerprint_ = GenerateFingerprint(x509_);\n}\n\nRTCCertificate::RTCCertificate(std::shared_ptr<X509> x509, std::shared_ptr<EVP_PKEY> evp_pkey)\n    : x509_(x509), evp_pkey_(evp_pkey), fingerprint_(GenerateFingerprint(x509_)) {}\n}\n"
  },
  {
    "path": "src/SCTPWrapper.cpp",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Wrapper around usrsctp/\n */\n\n#include \"rtcdcpp/SCTPWrapper.hpp\"\n\n#include <iostream>\n\nnamespace rtcdcpp {\n\nusing namespace std;\n\nSCTPWrapper::SCTPWrapper(DTLSEncryptCallbackPtr dtlsEncryptCB, MsgReceivedCallbackPtr msgReceivedCB)\n    : local_port(5000),  // XXX: Hard-coded for now\n      remote_port(5000),\n      stream_cursor(0),\n      dtlsEncryptCallback(dtlsEncryptCB),\n      msgReceivedCallback(msgReceivedCB) {}\n\nSCTPWrapper::~SCTPWrapper() {\n  Stop();\n\n  int tries = 0;\n  while (usrsctp_finish() != 0 && tries < 5) {\n    std::this_thread::sleep_for(std::chrono::milliseconds(1000));\n  }\n}\n\nstatic uint16_t interested_events[] = {SCTP_ASSOC_CHANGE,         SCTP_PEER_ADDR_CHANGE,   SCTP_REMOTE_ERROR,          SCTP_SEND_FAILED,\n                                       SCTP_SENDER_DRY_EVENT,     SCTP_SHUTDOWN_EVENT,     SCTP_ADAPTATION_INDICATION, SCTP_PARTIAL_DELIVERY_EVENT,\n                                       SCTP_AUTHENTICATION_EVENT, SCTP_STREAM_RESET_EVENT, SCTP_ASSOC_RESET_EVENT,     SCTP_STREAM_CHANGE_EVENT,\n                                       SCTP_SEND_FAILED_EVENT};\n\n// TODO: error callbacks\nvoid SCTPWrapper::OnNotification(union sctp_notification *notify, size_t len) {\n  if (notify->sn_header.sn_length != (uint32_t)len) {\n    logger->error(\"OnNotification(len={}) invalid length: {}\", len, notify->sn_header.sn_length);\n    return;\n  }\n\n  switch (notify->sn_header.sn_type) {\n    case SCTP_ASSOC_CHANGE:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_ASSOC_CHANGE)\");\n      break;\n    case SCTP_PEER_ADDR_CHANGE:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_PEER_ADDR_CHANGE)\");\n      break;\n    case SCTP_REMOTE_ERROR:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_REMOTE_ERROR)\");\n      break;\n    case SCTP_SEND_FAILED_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_SEND_FAILED_EVENT)\");\n      break;\n    case SCTP_SHUTDOWN_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_SHUTDOWN_EVENT)\");\n      break;\n    case SCTP_ADAPTATION_INDICATION:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_ADAPTATION_INDICATION)\");\n      break;\n    case SCTP_PARTIAL_DELIVERY_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_PARTIAL_DELIVERY_EVENT)\");\n      break;\n    case SCTP_AUTHENTICATION_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_AUTHENTICATION_EVENT)\");\n      break;\n    case SCTP_SENDER_DRY_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_SENDER_DRY_EVENT)\");\n      break;\n    case SCTP_NOTIFICATIONS_STOPPED_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_NOTIFICATIONS_STOPPED_EVENT)\");\n      break;\n    case SCTP_STREAM_RESET_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_STREAM_RESET_EVENT)\");\n      break;\n    case SCTP_ASSOC_RESET_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_ASSOC_RESET_EVENT)\");\n      break;\n    case SCTP_STREAM_CHANGE_EVENT:\n      SPDLOG_TRACE(logger, \"OnNotification(type=SCTP_STREAM_CHANGE_EVENT)\");\n      break;\n    default:\n      SPDLOG_TRACE(logger, \"OnNotification(type={} (unknown))\", notify->sn_header.sn_type);\n      break;\n  }\n}\n\nint SCTPWrapper::_OnSCTPForDTLS(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df) {\n  if (sctp_ptr) {\n    return static_cast<SCTPWrapper *>(sctp_ptr)->OnSCTPForDTLS(data, len, tos, set_df);\n  } else {\n    return -1;\n  }\n}\n\nint SCTPWrapper::OnSCTPForDTLS(void *data, size_t len, uint8_t tos, uint8_t set_df) {\n  SPDLOG_TRACE(logger, \"Data ready. len={}, tos={}, set_df={}\", len, tos, set_df);\n  this->dtlsEncryptCallback(std::make_shared<Chunk>(data, len));\n\n  {\n    unique_lock<mutex> l(connectMtx);\n    this->connectSentData = true;\n    connectCV.notify_one();\n  }\n\n  return 0;  // success\n}\n\nvoid SCTPWrapper::_DebugLog(const char *format, ...) {\n  va_list ap;\n  va_start(ap, format);\n  // std::string msg = Util::FormatString(format, ap);\n  char msg[1024 * 16];\n  vsprintf(msg, format, ap);\n  GetLogger(\"librtcpp.SCTP\")->trace(\"SCTP: msg={}\", msg);\n  va_end(ap);\n}\n\nint SCTPWrapper::_OnSCTPForGS(struct socket *sock, union sctp_sockstore addr, void *data, size_t len, struct sctp_rcvinfo recv_info, int flags,\n                              void *user_data) {\n  if (user_data) {\n    return static_cast<SCTPWrapper *>(user_data)->OnSCTPForGS(sock, addr, data, len, recv_info, flags);\n  } else {\n    return -1;\n  }\n}\n\nint SCTPWrapper::OnSCTPForGS(struct socket *sock, union sctp_sockstore addr, void *data, size_t len, struct sctp_rcvinfo recv_info, int flags) {\n  if (len == 0) {\n    return -1;\n  }\n\n  SPDLOG_TRACE(logger, \"Data received. stream={}, len={}, SSN={}, TSN={}, PPID={}\",\n                len,\n                recv_info.rcv_sid,\n                recv_info.rcv_ssn,\n                recv_info.rcv_tsn,\n                ntohl(recv_info.rcv_ppid));\n\n  if (flags & MSG_NOTIFICATION) {\n    OnNotification((union sctp_notification *)data, len);\n  } else {\n    std::cout << \"Got msg of size: \" << len << \"\\n\";\n    OnMsgReceived((const uint8_t *)data, len, recv_info.rcv_sid, ntohl(recv_info.rcv_ppid));\n  }\n  free(data);\n  return 0;\n}\n\nvoid SCTPWrapper::OnMsgReceived(const uint8_t *data, size_t len, int ppid, int sid) {\n  this->msgReceivedCallback(std::make_shared<Chunk>(data, len), ppid, sid);\n}\n\nbool SCTPWrapper::Initialize() {\n  usrsctp_init(0, &SCTPWrapper::_OnSCTPForDTLS, &SCTPWrapper::_DebugLog);\n  usrsctp_sysctl_set_sctp_ecn_enable(0);\n  usrsctp_register_address(this);\n\n  sock = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, &SCTPWrapper::_OnSCTPForGS, NULL, 0, this);\n  if (!sock) {\n    logger->error(\"Could not create usrsctp_socket. errno={}\", errno);\n    return false;\n  }\n\n  struct linger linger_opt;\n  linger_opt.l_onoff = 1;\n  linger_opt.l_linger = 0;\n  if (usrsctp_setsockopt(this->sock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt)) == -1) {\n    logger->error(\"Could not set socket options for SO_LINGER. errno={}\", errno);\n    return false;\n  }\n\n  struct sctp_paddrparams peer_param;\n  memset(&peer_param, 0, sizeof(peer_param));\n  peer_param.spp_flags = SPP_PMTUD_DISABLE;\n  peer_param.spp_pathmtu = 1200;  // XXX: Does this need to match the actual MTU?\n  if (usrsctp_setsockopt(this->sock, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &peer_param, sizeof(peer_param)) == -1) {\n    logger->error(\"Could not set socket options for SCTP_PEER_ADDR_PARAMS. errno={}\", errno);\n    return false;\n  }\n\n  struct sctp_assoc_value av;\n  av.assoc_id = SCTP_ALL_ASSOC;\n  av.assoc_value = 1;\n  if (usrsctp_setsockopt(this->sock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &av, sizeof(av)) == -1) {\n    logger->error(\"Could not set socket options for SCTP_ENABLE_STREAM_RESET. errno={}\", errno);\n    return false;\n  }\n\n  uint32_t nodelay = 1;\n  if (usrsctp_setsockopt(this->sock, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay)) == -1) {\n    logger->error(\"Could not set socket options for SCTP_NODELAY. errno={}\", errno);\n    return false;\n  }\n\n  /* Enable the events of interest */\n  struct sctp_event event;\n  memset(&event, 0, sizeof(event));\n  event.se_assoc_id = SCTP_ALL_ASSOC;\n  event.se_on = 1;\n  int num_events = sizeof(interested_events) / sizeof(uint16_t);\n  for (int i = 0; i < num_events; i++) {\n    event.se_type = interested_events[i];\n    if (usrsctp_setsockopt(this->sock, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) == -1) {\n      logger->error(\"Could not set socket options for SCTP_EVENT {}. errno={}\", i, errno);\n      return false;\n    }\n  }\n\n  struct sctp_initmsg init_msg;\n  memset(&init_msg, 0, sizeof(init_msg));\n  init_msg.sinit_num_ostreams = MAX_OUT_STREAM;\n  init_msg.sinit_max_instreams = MAX_IN_STREAM;\n  if (usrsctp_setsockopt(this->sock, IPPROTO_SCTP, SCTP_INITMSG, &init_msg, sizeof(init_msg)) == -1) {\n    logger->error(\"Could not set socket options for SCTP_INITMSG. errno={}\", errno);\n    return false;\n  }\n\n  struct sockaddr_conn sconn;\n  sconn.sconn_family = AF_CONN;\n  sconn.sconn_port = htons(remote_port);\n  sconn.sconn_addr = (void *)this;\n#ifdef HAVE_SCONN_LEN\n  sconn.sconn_len = sizeof(struct sockaddr_conn);\n#endif\n\n  if (usrsctp_bind(this->sock, (struct sockaddr *)&sconn, sizeof(sconn)) == -1) {\n    logger->error(\"Could not usrsctp_bind. errno={}\", errno);\n    return false;\n  }\n\n  return true;\n}\n\nvoid SCTPWrapper::Start() {\n  if (started) {\n    logger->error(\"Start() - already started!\");\n    return;\n  }\n\n  SPDLOG_TRACE(logger, \"Start()\");\n  started = true;\n\n  this->recv_thread = std::thread(&SCTPWrapper::RecvLoop, this);\n  this->connect_thread = std::thread(&SCTPWrapper::RunConnect, this);\n}\n\nvoid SCTPWrapper::Stop() {\n  this->should_stop = true;\n\n  send_queue.Stop();\n  recv_queue.Stop();\n\n  connectCV.notify_one();  // unblock the recv thread in case we never connected\n  if (this->recv_thread.joinable()) {\n    this->recv_thread.join();\n  }\n\n  if (this->connect_thread.joinable()) {\n    this->connect_thread.join();\n  }\n\n  if (sock) {\n    usrsctp_shutdown(sock, SHUT_RDWR);\n    usrsctp_close(sock);\n    sock = nullptr;\n  }\n}\n\nvoid SCTPWrapper::DTLSForSCTP(ChunkPtr chunk) { this->recv_queue.push(chunk); }\n\n// Send a message to the remote connection\nvoid SCTPWrapper::GSForSCTP(ChunkPtr chunk, uint16_t sid, uint32_t ppid) {\n  struct sctp_sendv_spa spa = {0};\n\n  // spa.sendv_flags = SCTP_SEND_SNDINFO_VALID | SCTP_SEND_PRINFO_VALID;\n  spa.sendv_flags = SCTP_SEND_SNDINFO_VALID;\n\n  spa.sendv_sndinfo.snd_sid = sid;\n  // spa.sendv_sndinfo.snd_flags = SCTP_EOR | SCTP_UNORDERED;\n  spa.sendv_sndinfo.snd_flags = SCTP_EOR;\n  spa.sendv_sndinfo.snd_ppid = htonl(ppid);\n\n  // spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;\n  // spa.sendv_prinfo.pr_value = 0;\n\n  int tries = 0;\n  while (tries < 5) {\n    if (usrsctp_sendv(this->sock, chunk->Data(), chunk->Length(), NULL, 0, &spa, sizeof(spa), SCTP_SENDV_SPA, 0) < 0) {\n      logger->error(\"FAILED to send, try: {}\", tries);\n      tries += 1;\n      std::this_thread::sleep_for(std::chrono::seconds(tries));\n    } else {\n      return;\n    }\n  }\n  //tried about 5 times and still no luck\n  throw std::runtime_error(\"Send failed\");\n}\n\nvoid SCTPWrapper::RecvLoop() {\n  // Util::SetThreadName(\"SCTP-RecvLoop\");\n//  NDC ndc(\"SCTP-RecvLoop\");\n\n  SPDLOG_TRACE(logger, \"RunRecv()\");\n\n  {\n    // We need to wait for the connect thread to send some data\n    unique_lock<mutex> l(connectMtx);\n    while (!this->connectSentData && !this->should_stop) {\n      connectCV.wait_for(l, chrono::milliseconds(100));\n    }\n  }\n\n  SPDLOG_DEBUG(logger, \"RunRecv() sent_data=true\");\n\n  while (!this->should_stop) {\n    ChunkPtr chunk = this->recv_queue.wait_and_pop();\n    if (!chunk) {\n      return;\n    }\n    SPDLOG_DEBUG(logger, \"RunRecv() Handling packet of len - {}\", chunk->Length());\n    usrsctp_conninput(this, chunk->Data(), chunk->Length(), 0);\n  }\n}\n\nvoid SCTPWrapper::RunConnect() {\n  // Util::SetThreadName(\"SCTP-Connect\");\n  SPDLOG_TRACE(logger, \"RunConnect() port={}\", remote_port);\n\n  struct sockaddr_conn sconn;\n  sconn.sconn_family = AF_CONN;\n  sconn.sconn_port = htons(remote_port);\n  sconn.sconn_addr = (void *)this;\n#ifdef HAVE_SCONN_LEN\n  sconn.sconn_len = sizeof((void *)this);\n#endif\n\n  // Blocks until connection succeeds/fails\n  int connect_result = usrsctp_connect(sock, (struct sockaddr *)&sconn, sizeof sconn);\n\n  if ((connect_result < 0) && (errno != EINPROGRESS)) {\n    SPDLOG_DEBUG(logger, \"Connection failed. errno={}\", errno);\n    should_stop = true;\n\n    {\n      // Unblock the recv thread\n      unique_lock<mutex> l(connectMtx);\n      connectCV.notify_one();\n    }\n\n    // TODO let the world know we failed :(\n\n  } else {\n    SPDLOG_DEBUG(logger, \"Connected on port {}\", remote_port);\n  }\n}\n}\n"
  },
  {
    "path": "src/librtcdcpp.c",
    "content": "/**\n * Copyright (c) 2017, Andrew Gault, Nick Chadwick and Guillaume Egles.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *    * Redistributions of source code must retain the above copyright\n *      notice, this list of conditions and the following disclaimer.\n *    * Redistributions in binary form must reproduce the above copyright\n *      notice, this list of conditions and the following disclaimer in the\n *      documentation and/or other materials provided with the distribution.\n *    * Neither the name of the <organization> nor the\n *      names of its contributors may be used to endorse or promote products\n *      derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * C Wrapper around the C++ classes.\n */\n"
  }
]