<commit_before>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <string>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include "util/exception.h"
#include "util/sstream.h"
#include "util/name.h"
#include "util/optional.h"
#include "util/realpath.h"

#ifndef LEAN_DEFAULT_MODULE_FILE_NAME
#define LEAN_DEFAULT_MODULE_FILE_NAME "default"
#endif

namespace lean {
static std::string g_default_file_name(LEAN_DEFAULT_MODULE_FILE_NAME);

bool is_directory(char const * pathname) {
    struct stat info;
    if (stat(pathname, &info) != 0 )
        return false; // failed to access pathname
    return info.st_mode & S_IFDIR;
}

#if defined(LEAN_WINDOWS)
// Windows version
#include <windows.h>
static char g_path_sep     = ';';
static char g_sep          = '\\';
static char g_bad_sep      = '/';
static std::string get_exe_location() {
    HMODULE hModule = GetModuleHandleW(NULL);
    WCHAR path[MAX_PATH];
    GetModuleFileNameW(hModule, path, MAX_PATH);
    std::wstring pathstr(path);
    return std::string(pathstr.begin(), pathstr.end());
}
#elif defined(__APPLE__)
// OSX version
#include <mach-o/dyld.h>
#include <limits.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char buf[PATH_MAX];
    uint32_t bufsize = PATH_MAX;
    if (_NSGetExecutablePath(buf, &bufsize) != 0)
        throw exception("failed to locate Lean executable location");
    return std::string(buf);
}
#else
// Linux version
#include <unistd.h>
#include <string.h>
#include <limits.h> // NOLINT
#include <stdio.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char path[PATH_MAX];
    char dest[PATH_MAX];
    memset(dest, 0, PATH_MAX);
    pid_t pid = getpid();
    snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
    if (readlink(path, dest, PATH_MAX) == -1) {
        throw exception("failed to locate Lean executable location");
    } else {
        return std::string(dest);
    }
}
#endif

#if defined(LEAN_CYGWIN)
/**
   \brief Cleanup path when using cygwin.
   This procedure performs two fixes, if the string contains '/'.

   1- It replaces '/' with '\\' and ':' with ';'

   2- Then, it replaces "\cygdrive\c\" with "c:"
*/
static std::string fix_cygwin_path(std::string f) {
    if (f.find('/') != std::string::npos) {
        // Step 1.
        for (auto & c : f) {
            if (c == g_bad_sep)
                c = g_sep;
            else if (c == ':')
                c = g_path_sep;
        }
        // Step 2.
        size_t pos = 0;
        size_t matchpos;
        std::string cygdrive("\\cygdrive\\");
        while ((matchpos = f.find(cygdrive, pos)) != std::string::npos) {
            // erase "\cygdrive\"
            f = f.replace(matchpos, cygdrive.size(), "");
            // replace the next "\" with ":\"
            if ((matchpos = f.find("\\", matchpos)) != std::string::npos) {
                f.replace(matchpos, 1, ":\\");
            }
            pos = matchpos;
        }
    }
    return f;
}

/** \brief Convert back to a cygwin_path */
static std::string to_cygwin_path(std::string f) {
    if (f.find('\\') != std::string::npos) {
        if (f.size() > 2 && f[1] == ':') {
            f = std::string("/cygdrive/") + f[0] + f.substr(2);
        }
        for (auto & c : f) {
            if (c == g_sep)
                c = g_bad_sep;
            else if (c == ';')
                c = ':';
        }
    }
    return f;
}
#endif

std::string normalize_path(std::string f) {
#if defined(LEAN_CYGWIN)
    f = fix_cygwin_path(f);
#else
    for (auto & c : f) {
        if (c == g_bad_sep)
            c = g_sep;
    }
#endif
    return f;
}

std::string get_path(std::string f) {
    while (true) {
        if (f.empty())
            throw exception("failed to locate Lean executable location");
        if (f.back() == g_sep) {
            f.pop_back();
            return f;
        }
        f.pop_back();
    }
}

static std::string              g_lean_path;
static std::vector<std::string> g_lean_path_vector;
void init_lean_path(char const * kernel_instance_name) {
    char * r = getenv("LEAN_PATH");
    if (r == nullptr) {
        g_lean_path  = ".";
        std::string exe_path = get_path(get_exe_location());
        g_lean_path += g_path_sep;
        g_lean_path += exe_path + g_sep + ".." + g_sep + "library" + g_sep + kernel_instance_name;
    } else {
        g_lean_path = r;
    }
    g_lean_path_vector.clear();
    g_lean_path = normalize_path(g_lean_path);
    unsigned i  = 0;
    unsigned j  = 0;
    unsigned sz = g_lean_path.size();
    for (; j < sz; j++) {
        if (g_lean_path[j] == g_path_sep) {
            if (j > i)
                g_lean_path_vector.push_back(g_lean_path.substr(i, j - i));
            i = j + 1;
        }
    }
    if (j > i)
        g_lean_path_vector.push_back(g_lean_path.substr(i, j - i));
}

struct init_lean_path_fn {
    init_lean_path_fn() { init_lean_path("standard"); }
};
static init_lean_path_fn g_init_lean_path_fn;
static std::string       g_sep_str(1, g_sep);

bool has_file_ext(std::string const & fname, char const * ext) {
    unsigned ext_len = strlen(ext);
    return fname.size() > ext_len && fname.substr(fname.size() - ext_len, ext_len) == ext;
}

bool is_lean_file(std::string const & fname) {
    return has_file_ext(fname, ".lean");
}

bool is_olean_file(std::string const & fname) {
    return has_file_ext(fname, ".olean");
}

bool is_lua_file(std::string const & fname) {
    return has_file_ext(fname, ".lua");
}

bool is_known_file_ext(std::string const & fname) {
    return is_lean_file(fname) || is_olean_file(fname) || is_lua_file(fname);
}

optional<std::string> check_file(std::string const & path, std::string const & fname, char const * ext = nullptr) {
    std::string file = path + g_sep + fname;
    if (is_directory(file.c_str()))
        file += g_sep + g_default_file_name;
    if (ext)
        file += ext;
    std::ifstream ifile(file);
    if (ifile)
        return optional<std::string>(realpath(file.c_str()));
    else
        return optional<std::string>();
}

std::string name_to_file(name const & fname) {
    return fname.to_string(g_sep_str.c_str());
}

std::string find_file(std::string fname, std::initializer_list<char const *> const & extensions) {
    bool is_known = is_known_file_ext(fname);
    fname = normalize_path(fname);
    for (auto path : g_lean_path_vector) {
        if (is_known) {
            if (auto r = check_file(path, fname))
                return *r;
        } else {
            for (auto ext : extensions) {
                if (auto r = check_file(path, fname, ext))
                    return *r;
            }
        }
    }
    throw exception(sstream() << "file '" << fname << "' not found in the LEAN_PATH");
}

std::string find_file(std::string const & base, optional<unsigned> const & rel, name const & fname,
                      std::initializer_list<char const *> const & extensions) {
    if (!rel) {
        return find_file(fname.to_string(g_sep_str.c_str()), extensions);
    } else {
        auto path = base;
        for (unsigned i = 0; i < *rel; i++) {
            path += g_sep;
            path += "..";
        }
        for (auto ext : extensions) {
            if (auto r = check_file(path, fname.to_string(g_sep_str.c_str()), ext))
                return *r;
        }
        throw exception(sstream() << "file '" << fname << "' not found at '" << path << "'");
    }
}

std::string find_file(std::string const & base, optional<unsigned> const & k, name const & fname, char const * ext) {
    return find_file(base, k, fname, {ext});
}

std::string find_file(std::string fname) {
    return find_file(fname, {".olean", ".lean", ".lua"});
}

std::string find_file(name const & fname) {
    return find_file(fname.to_string(g_sep_str.c_str()));
}

std::string find_file(name const & fname, std::initializer_list<char const *> const & exts) {
    return find_file(fname.to_string(g_sep_str.c_str()), exts);
}

char const * get_lean_path() {
    return g_lean_path.c_str();
}

void display_path(std::ostream & out, std::string const & fname) {
#if defined(LEAN_CYGWIN)
    out << to_cygwin_path(fname);
#else
    out << fname;
#endif
}

std::string dirname(char const * fname) {
    if (fname == nullptr)
        return ".";
    unsigned i = 0;
    optional<unsigned> last_sep;
    char const * it = fname;
    while (*it) {
        if (*it == g_sep)
            last_sep = i;
        ++i;
        ++it;
    }
    if (!last_sep)
        return ".";
    std::string r;
    for (unsigned i = 0; i < *last_sep; i++)
        r.push_back(fname[i]);
    return r;
}

std::string path_append(char const * p1, char const * p2) {
    std::string r(p1);
    r += g_sep;
    r += p2;
    return r;
}
}
<commit_msg>chore(util/lean_path): workaround 'spurious' warning produced by g++ in release mode<commit_after>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <string>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include "util/exception.h"
#include "util/sstream.h"
#include "util/name.h"
#include "util/optional.h"
#include "util/realpath.h"

#ifndef LEAN_DEFAULT_MODULE_FILE_NAME
#define LEAN_DEFAULT_MODULE_FILE_NAME "default"
#endif

namespace lean {
static std::string g_default_file_name(LEAN_DEFAULT_MODULE_FILE_NAME);

bool is_directory(char const * pathname) {
    struct stat info;
    if (stat(pathname, &info) != 0 )
        return false; // failed to access pathname
    return info.st_mode & S_IFDIR;
}

#if defined(LEAN_WINDOWS)
// Windows version
#include <windows.h>
static char g_path_sep     = ';';
static char g_sep          = '\\';
static char g_bad_sep      = '/';
static std::string get_exe_location() {
    HMODULE hModule = GetModuleHandleW(NULL);
    WCHAR path[MAX_PATH];
    GetModuleFileNameW(hModule, path, MAX_PATH);
    std::wstring pathstr(path);
    return std::string(pathstr.begin(), pathstr.end());
}
#elif defined(__APPLE__)
// OSX version
#include <mach-o/dyld.h>
#include <limits.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char buf[PATH_MAX];
    uint32_t bufsize = PATH_MAX;
    if (_NSGetExecutablePath(buf, &bufsize) != 0)
        throw exception("failed to locate Lean executable location");
    return std::string(buf);
}
#else
// Linux version
#include <unistd.h>
#include <string.h>
#include <limits.h> // NOLINT
#include <stdio.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char path[PATH_MAX];
    char dest[PATH_MAX];
    memset(dest, 0, PATH_MAX);
    pid_t pid = getpid();
    snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
    if (readlink(path, dest, PATH_MAX) == -1) {
        throw exception("failed to locate Lean executable location");
    } else {
        return std::string(dest);
    }
}
#endif

#if defined(LEAN_CYGWIN)
/**
   \brief Cleanup path when using cygwin.
   This procedure performs two fixes, if the string contains '/'.

   1- It replaces '/' with '\\' and ':' with ';'

   2- Then, it replaces "\cygdrive\c\" with "c:"
*/
static std::string fix_cygwin_path(std::string f) {
    if (f.find('/') != std::string::npos) {
        // Step 1.
        for (auto & c : f) {
            if (c == g_bad_sep)
                c = g_sep;
            else if (c == ':')
                c = g_path_sep;
        }
        // Step 2.
        size_t pos = 0;
        size_t matchpos;
        std::string cygdrive("\\cygdrive\\");
        while ((matchpos = f.find(cygdrive, pos)) != std::string::npos) {
            // erase "\cygdrive\"
            f = f.replace(matchpos, cygdrive.size(), "");
            // replace the next "\" with ":\"
            if ((matchpos = f.find("\\", matchpos)) != std::string::npos) {
                f.replace(matchpos, 1, ":\\");
            }
            pos = matchpos;
        }
    }
    return f;
}

/** \brief Convert back to a cygwin_path */
static std::string to_cygwin_path(std::string f) {
    if (f.find('\\') != std::string::npos) {
        if (f.size() > 2 && f[1] == ':') {
            f = std::string("/cygdrive/") + f[0] + f.substr(2);
        }
        for (auto & c : f) {
            if (c == g_sep)
                c = g_bad_sep;
            else if (c == ';')
                c = ':';
        }
    }
    return f;
}
#endif

std::string normalize_path(std::string f) {
#if defined(LEAN_CYGWIN)
    f = fix_cygwin_path(f);
#else
    for (auto & c : f) {
        if (c == g_bad_sep)
            c = g_sep;
    }
#endif
    return f;
}

std::string get_path(std::string f) {
    while (true) {
        if (f.empty())
            throw exception("failed to locate Lean executable location");
        if (f.back() == g_sep) {
            f.pop_back();
            return f;
        }
        f.pop_back();
    }
}

static std::string              g_lean_path;
static std::vector<std::string> g_lean_path_vector;
void init_lean_path(char const * kernel_instance_name) {
    char * r = getenv("LEAN_PATH");
    if (r == nullptr) {
        g_lean_path  = ".";
        std::string exe_path = get_path(get_exe_location());
        g_lean_path += g_path_sep;
        g_lean_path += exe_path + g_sep + ".." + g_sep + "library" + g_sep + kernel_instance_name;
    } else {
        g_lean_path = r;
    }
    g_lean_path_vector.clear();
    g_lean_path = normalize_path(g_lean_path);
    unsigned i  = 0;
    unsigned j  = 0;
    unsigned sz = g_lean_path.size();
    for (; j < sz; j++) {
        if (g_lean_path[j] == g_path_sep) {
            if (j > i)
                g_lean_path_vector.push_back(g_lean_path.substr(i, j - i));
            i = j + 1;
        }
    }
    if (j > i)
        g_lean_path_vector.push_back(g_lean_path.substr(i, j - i));
}

struct init_lean_path_fn {
    init_lean_path_fn() { init_lean_path("standard"); }
};
static init_lean_path_fn g_init_lean_path_fn;
static std::string       g_sep_str(1, g_sep);

bool has_file_ext(std::string const & fname, char const * ext) {
    unsigned ext_len = strlen(ext);
    return fname.size() > ext_len && fname.substr(fname.size() - ext_len, ext_len) == ext;
}

bool is_lean_file(std::string const & fname) {
    return has_file_ext(fname, ".lean");
}

bool is_olean_file(std::string const & fname) {
    return has_file_ext(fname, ".olean");
}

bool is_lua_file(std::string const & fname) {
    return has_file_ext(fname, ".lua");
}

bool is_known_file_ext(std::string const & fname) {
    return is_lean_file(fname) || is_olean_file(fname) || is_lua_file(fname);
}

optional<std::string> check_file(std::string const & path, std::string const & fname, char const * ext = nullptr) {
    std::string file = path + g_sep + fname;
    if (is_directory(file.c_str()))
        file += g_sep + g_default_file_name;
    if (ext)
        file += ext;
    std::ifstream ifile(file);
    if (ifile)
        return optional<std::string>(realpath(file.c_str()));
    else
        return optional<std::string>();
}

std::string name_to_file(name const & fname) {
    return fname.to_string(g_sep_str.c_str());
}

std::string find_file(std::string fname, std::initializer_list<char const *> const & extensions) {
    bool is_known = is_known_file_ext(fname);
    fname = normalize_path(fname);
    for (auto path : g_lean_path_vector) {
        if (is_known) {
            if (auto r = check_file(path, fname))
                return *r;
        } else {
            for (auto ext : extensions) {
                if (auto r = check_file(path, fname, ext))
                    return *r;
            }
        }
    }
    throw exception(sstream() << "file '" << fname << "' not found in the LEAN_PATH");
}

std::string find_file(std::string const & base, optional<unsigned> const & rel, name const & fname,
                      std::initializer_list<char const *> const & extensions) {
    if (!rel) {
        return find_file(fname.to_string(g_sep_str.c_str()), extensions);
    } else {
        auto path = base;
        for (unsigned i = 0; i < *rel; i++) {
            path += g_sep;
            path += "..";
        }
        for (auto ext : extensions) {
            if (auto r = check_file(path, fname.to_string(g_sep_str.c_str()), ext))
                return *r;
        }
        throw exception(sstream() << "file '" << fname << "' not found at '" << path << "'");
    }
}

std::string find_file(std::string const & base, optional<unsigned> const & k, name const & fname, char const * ext) {
    return find_file(base, k, fname, {ext});
}

std::string find_file(std::string fname) {
    return find_file(fname, {".olean", ".lean", ".lua"});
}

std::string find_file(name const & fname) {
    return find_file(fname.to_string(g_sep_str.c_str()));
}

std::string find_file(name const & fname, std::initializer_list<char const *> const & exts) {
    return find_file(fname.to_string(g_sep_str.c_str()), exts);
}

char const * get_lean_path() {
    return g_lean_path.c_str();
}

void display_path(std::ostream & out, std::string const & fname) {
#if defined(LEAN_CYGWIN)
    out << to_cygwin_path(fname);
#else
    out << fname;
#endif
}

std::string dirname(char const * fname) {
    if (fname == nullptr)
        return ".";
    unsigned i        = 0;
    unsigned last_sep = 0;
    bool found_sep    = false;
    char const * it   = fname;
    while (*it) {
        if (*it == g_sep) {
            found_sep = true;
            last_sep  = i;
        }
        ++i;
        ++it;
    }
    if (!found_sep) {
        return ".";
    } else {
        std::string r;
        for (unsigned i = 0; i < last_sep; i++)
            r.push_back(fname[i]);
        return r;
    }
}

std::string path_append(char const * p1, char const * p2) {
    std::string r(p1);
    r += g_sep;
    r += p2;
    return r;
}
}
<|endoftext|>


<commit_before>/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2005-2006 Refractions Research Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 *
 * Last port: io/WKBReader.java rev. 1.1 (JTS-1.7)
 *
 **********************************************************************/

#include <geos/io/WKBReader.h>
#include <geos/io/WKBConstants.h>
#include <geos/io/ByteOrderValues.h>
#include <geos/io/ParseException.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/Point.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/LineString.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/MultiPoint.h>
#include <geos/geom/MultiLineString.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/CoordinateSequenceFactory.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/PrecisionModel.h>

#include <iomanip>
#include <ostream>
#include <sstream>
#include <string>

//#define DEBUG_WKB_READER 1

using namespace std;
using namespace geos::geom;

namespace geos {
namespace io { // geos.io

WKBReader::WKBReader()
    :
    factory(*(GeometryFactory::getDefaultInstance()))
{}

ostream&
WKBReader::printHEX(istream& is, ostream& os)
{
    static const char hex[] = "0123456789ABCDEF";

    std::streampos pos = is.tellg(); // take note of input stream get pointer
    is.seekg(0, ios::beg); // rewind input stream

    char each = 0;
    while(is.read(&each, 1)) {
        const unsigned char c = each;
        int low = (c & 0x0F);
        int high = (c >> 4);
        os << hex[high] << hex[low];
    }

    is.clear(); // clear input stream eof flag
    is.seekg(pos); // reset input stream position

    return os;
}

namespace {

unsigned char
ASCIIHexToUChar(char val)
{
    switch(val) {
    case '0' :
        return 0;
    case '1' :
        return 1;
    case '2' :
        return 2;
    case '3' :
        return 3;
    case '4' :
        return 4;
    case '5' :
        return 5;
    case '6' :
        return 6;
    case '7' :
        return 7;
    case '8' :
        return 8;
    case '9' :
        return 9;
    case 'A' :
    case 'a' :
        return 10;
    case 'B' :
    case 'b' :
        return 11;
    case 'C' :
    case 'c' :
        return 12;
    case 'D' :
    case 'd' :
        return 13;
    case 'E' :
    case 'e' :
        return 14;
    case 'F' :
    case 'f' :
        return 15;
    default:
        throw ParseException("Invalid HEX char");
    }
}

}  // namespace

// Must be an even number of characters in the istream.
// Throws a ParseException if there are an odd number of characters.
std::unique_ptr<Geometry>
WKBReader::readHEX(istream& is)
{
    // setup input/output stream
    stringstream os(ios_base::binary | ios_base::in | ios_base::out);

    while(true) {
        const int input_high = is.get();
        if(input_high == char_traits<char>::eof()) {
            break;
        }

        const int input_low = is.get();
        if(input_low == char_traits<char>::eof()) {
            throw ParseException("Premature end of HEX string");
        }

        const char high = static_cast<char>(input_high);
        const char low = static_cast<char>(input_low);

        const unsigned char result_high = ASCIIHexToUChar(high);
        const unsigned char result_low = ASCIIHexToUChar(low);

        const unsigned char value =
            static_cast<char>((result_high << 4) + result_low);

#if DEBUG_HEX_READER
        cout << "HEX " << high << low << " -> DEC " << (int)value << endl;
#endif
        // write the value to the output stream
        os << value;
    }

    // now call read to convert the geometry
    return this->read(os);
}

std::unique_ptr<Geometry>
WKBReader::read(istream& is)
{
    dis.setInStream(&is); // will default to machine endian
    return readGeometry();
}

std::unique_ptr<Geometry>
WKBReader::readGeometry()
{
    // determine byte order
    unsigned char byteOrder = dis.readByte();

#if DEBUG_WKB_READER
    cout << "WKB byteOrder: " << (int)byteOrder << endl;
#endif

    // default is machine endian
    if(byteOrder == WKBConstants::wkbNDR) {
        dis.setOrder(ByteOrderValues::ENDIAN_LITTLE);
    }
    else if(byteOrder == WKBConstants::wkbXDR) {
        dis.setOrder(ByteOrderValues::ENDIAN_BIG);
    }

    int typeInt = dis.readInt();
    /* Pick up both ISO and SFSQL geometry type */
    int geometryType = (typeInt & 0xffff) % 1000;
    /* ISO type range 1000 is Z, 2000 is M, 3000 is ZM */
    int isoTypeRange = (typeInt & 0xffff) / 1000;
    int isoHasZ = (isoTypeRange == 1) || (isoTypeRange == 3);
    //int isoHasM = (isoTypeRange == 2) || (isoTypeRange == 3);
    /* SFSQL high bit flag for Z, next bit for M */
    int sfsqlHasZ = (typeInt & 0x80000000) != 0;
    //int sfsqlHasM = (typeInt & 0x40000000) != 0;

#if DEBUG_WKB_READER
    cout << "WKB geometryType: " << geometryType << endl;
#endif

    bool hasZ = sfsqlHasZ || isoHasZ;
    if(hasZ) {
        inputDimension = 3;
    }
    else {
        inputDimension = 2;    // doesn't handle M currently
    }

#if DEBUG_WKB_READER
    cout << "WKB hasZ: " << hasZ << endl;
#endif

#if DEBUG_WKB_READER
    cout << "WKB dimensions: " << inputDimension << endl;
#endif

    bool hasSRID = ((typeInt & 0x20000000) != 0);

#if DEBUG_WKB_READER
    cout << "WKB hasSRID: " << hasSRID << endl;
#endif

    int SRID = 0;
    if(hasSRID) {
        SRID = dis.readInt();    // read SRID
    }

    // allocate space for ordValues
    if(ordValues.size() < inputDimension) {
        ordValues.resize(inputDimension);
    }

    std::unique_ptr<Geometry> result;

    switch(geometryType) {
    case WKBConstants::wkbPoint :
        result = readPoint();
        break;
    case WKBConstants::wkbLineString :
        result = readLineString();
        break;
    case WKBConstants::wkbPolygon :
        result = readPolygon();
        break;
    case WKBConstants::wkbMultiPoint :
        result = readMultiPoint();
        break;
    case WKBConstants::wkbMultiLineString :
        result = readMultiLineString();
        break;
    case WKBConstants::wkbMultiPolygon :
        result = readMultiPolygon();
        break;
    case WKBConstants::wkbGeometryCollection :
        result = readGeometryCollection();
        break;
    default:
        stringstream err;
        err << "Unknown WKB type " << geometryType;
        throw  ParseException(err.str());
    }

    result->setSRID(SRID);
    return result;
}

std::unique_ptr<Point>
WKBReader::readPoint()
{
    readCoordinate();
    if(inputDimension == 3) {
        return std::unique_ptr<Point>(factory.createPoint(Coordinate(ordValues[0], ordValues[1], ordValues[2])));
    }
    else {
        return std::unique_ptr<Point>(factory.createPoint(Coordinate(ordValues[0], ordValues[1])));
    }
}

std::unique_ptr<LineString>
WKBReader::readLineString()
{
    int size = dis.readInt();
#if DEBUG_WKB_READER
    cout << "WKB npoints: " << size << endl;
#endif
    auto pts = readCoordinateSequence(size);
    return std::unique_ptr<LineString>(factory.createLineString(pts.release()));
}

std::unique_ptr<LinearRing>
WKBReader::readLinearRing()
{
    int size = dis.readInt();
#if DEBUG_WKB_READER
    cout << "WKB npoints: " << size << endl;
#endif
    auto pts = readCoordinateSequence(size);
    return std::unique_ptr<LinearRing>(factory.createLinearRing(pts.release()));
}

std::unique_ptr<Polygon>
WKBReader::readPolygon()
{
    int numRings = dis.readInt();

#if DEBUG_WKB_READER
    cout << "WKB numRings: " << numRings << endl;
#endif

    std::unique_ptr<LinearRing> shell;
    if(numRings > 0) {
        shell = readLinearRing();
    }

    if(numRings > 1) {
        std::vector<std::unique_ptr<LinearRing>> holes(numRings - 1);
        for(int i = 0; i < numRings - 1; i++) {
            holes[i] = readLinearRing();
        }

        return factory.createPolygon(std::move(shell), std::move(holes));
    }
    return factory.createPolygon(std::move(shell));
}

std::unique_ptr<MultiPoint>
WKBReader::readMultiPoint()
{
    int numGeoms = dis.readInt();
    std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);

    for(int i = 0; i < numGeoms; i++) {
        geoms[i] = readGeometry();
        if(!dynamic_cast<Point*>(geoms[i].get())) {
            stringstream err;
            err << BAD_GEOM_TYPE_MSG << " MultiPoint";
            throw ParseException(err.str());
        }
    }

    return factory.createMultiPoint(std::move(geoms));
}

std::unique_ptr<MultiLineString>
WKBReader::readMultiLineString()
{
    int numGeoms = dis.readInt();
    std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);

    for(int i = 0; i < numGeoms; i++) {
        geoms[i] = readGeometry();
        if(!dynamic_cast<LineString*>(geoms[i].get())) {
            stringstream err;
            err << BAD_GEOM_TYPE_MSG << " LineString";
            throw  ParseException(err.str());
        }
    }

    return factory.createMultiLineString(std::move(geoms));
}

std::unique_ptr<MultiPolygon>
WKBReader::readMultiPolygon()
{
    int numGeoms = dis.readInt();
    std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);

    for(int i = 0; i < numGeoms; i++) {
        geoms[i] = readGeometry();
        if(!dynamic_cast<Polygon*>(geoms[i].get())) {
            stringstream err;
            err << BAD_GEOM_TYPE_MSG << " Polygon";
            throw  ParseException(err.str());
        }
    }

    return factory.createMultiPolygon(std::move(geoms));
}

std::unique_ptr<GeometryCollection>
WKBReader::readGeometryCollection()
{
    int numGeoms = dis.readInt();
    std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);

    for(int i = 0; i < numGeoms; i++) {
        geoms[i] = readGeometry();
    }

    return factory.createGeometryCollection(std::move(geoms));
}

std::unique_ptr<CoordinateSequence>
WKBReader::readCoordinateSequence(int size)
{
    auto seq = factory.getCoordinateSequenceFactory()->create(size, inputDimension);
    auto targetDim = seq->getDimension();
    if(targetDim > inputDimension) {
        targetDim = inputDimension;
    }
    for(int i = 0; i < size; i++) {
        readCoordinate();
        for(unsigned int j = 0; j < targetDim; j++) {
            seq->setOrdinate(i, j, ordValues[j]);
        }
    }
    return seq;
}

void
WKBReader::readCoordinate()
{
    const PrecisionModel& pm = *factory.getPrecisionModel();
    for(unsigned int i = 0; i < inputDimension; ++i) {
        if(i <= 1) {
            ordValues[i] = pm.makePrecise(dis.readDouble());
        }
        else {
            ordValues[i] = dis.readDouble();
        }
    }
#if DEBUG_WKB_READER
    cout << "WKB coordinate: " << ordValues[0] << "," << ordValues[1] << endl;
#endif
}

} // namespace geos.io
} // namespace geos
<commit_msg>Use simpler GeometryFactory methods in WKBReader<commit_after>/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2005-2006 Refractions Research Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 *
 * Last port: io/WKBReader.java rev. 1.1 (JTS-1.7)
 *
 **********************************************************************/

#include <geos/io/WKBReader.h>
#include <geos/io/WKBConstants.h>
#include <geos/io/ByteOrderValues.h>
#include <geos/io/ParseException.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/Point.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/LineString.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/MultiPoint.h>
#include <geos/geom/MultiLineString.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/CoordinateSequenceFactory.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/PrecisionModel.h>

#include <iomanip>
#include <ostream>
#include <sstream>
#include <string>

//#define DEBUG_WKB_READER 1

using namespace std;
using namespace geos::geom;

namespace geos {
namespace io { // geos.io

WKBReader::WKBReader()
    :
    factory(*(GeometryFactory::getDefaultInstance()))
{}

ostream&
WKBReader::printHEX(istream& is, ostream& os)
{
    static const char hex[] = "0123456789ABCDEF";

    std::streampos pos = is.tellg(); // take note of input stream get pointer
    is.seekg(0, ios::beg); // rewind input stream

    char each = 0;
    while(is.read(&each, 1)) {
        const unsigned char c = each;
        int low = (c & 0x0F);
        int high = (c >> 4);
        os << hex[high] << hex[low];
    }

    is.clear(); // clear input stream eof flag
    is.seekg(pos); // reset input stream position

    return os;
}

namespace {

unsigned char
ASCIIHexToUChar(char val)
{
    switch(val) {
    case '0' :
        return 0;
    case '1' :
        return 1;
    case '2' :
        return 2;
    case '3' :
        return 3;
    case '4' :
        return 4;
    case '5' :
        return 5;
    case '6' :
        return 6;
    case '7' :
        return 7;
    case '8' :
        return 8;
    case '9' :
        return 9;
    case 'A' :
    case 'a' :
        return 10;
    case 'B' :
    case 'b' :
        return 11;
    case 'C' :
    case 'c' :
        return 12;
    case 'D' :
    case 'd' :
        return 13;
    case 'E' :
    case 'e' :
        return 14;
    case 'F' :
    case 'f' :
        return 15;
    default:
        throw ParseException("Invalid HEX char");
    }
}

}  // namespace

// Must be an even number of characters in the istream.
// Throws a ParseException if there are an odd number of characters.
std::unique_ptr<Geometry>
WKBReader::readHEX(istream& is)
{
    // setup input/output stream
    stringstream os(ios_base::binary | ios_base::in | ios_base::out);

    while(true) {
        const int input_high = is.get();
        if(input_high == char_traits<char>::eof()) {
            break;
        }

        const int input_low = is.get();
        if(input_low == char_traits<char>::eof()) {
            throw ParseException("Premature end of HEX string");
        }

        const char high = static_cast<char>(input_high);
        const char low = static_cast<char>(input_low);

        const unsigned char result_high = ASCIIHexToUChar(high);
        const unsigned char result_low = ASCIIHexToUChar(low);

        const unsigned char value =
            static_cast<char>((result_high << 4) + result_low);

#if DEBUG_HEX_READER
        cout << "HEX " << high << low << " -> DEC " << (int)value << endl;
#endif
        // write the value to the output stream
        os << value;
    }

    // now call read to convert the geometry
    return this->read(os);
}

std::unique_ptr<Geometry>
WKBReader::read(istream& is)
{
    dis.setInStream(&is); // will default to machine endian
    return readGeometry();
}

std::unique_ptr<Geometry>
WKBReader::readGeometry()
{
    // determine byte order
    unsigned char byteOrder = dis.readByte();

#if DEBUG_WKB_READER
    cout << "WKB byteOrder: " << (int)byteOrder << endl;
#endif

    // default is machine endian
    if(byteOrder == WKBConstants::wkbNDR) {
        dis.setOrder(ByteOrderValues::ENDIAN_LITTLE);
    }
    else if(byteOrder == WKBConstants::wkbXDR) {
        dis.setOrder(ByteOrderValues::ENDIAN_BIG);
    }

    int typeInt = dis.readInt();
    /* Pick up both ISO and SFSQL geometry type */
    int geometryType = (typeInt & 0xffff) % 1000;
    /* ISO type range 1000 is Z, 2000 is M, 3000 is ZM */
    int isoTypeRange = (typeInt & 0xffff) / 1000;
    int isoHasZ = (isoTypeRange == 1) || (isoTypeRange == 3);
    //int isoHasM = (isoTypeRange == 2) || (isoTypeRange == 3);
    /* SFSQL high bit flag for Z, next bit for M */
    int sfsqlHasZ = (typeInt & 0x80000000) != 0;
    //int sfsqlHasM = (typeInt & 0x40000000) != 0;

#if DEBUG_WKB_READER
    cout << "WKB geometryType: " << geometryType << endl;
#endif

    bool hasZ = sfsqlHasZ || isoHasZ;
    if(hasZ) {
        inputDimension = 3;
    }
    else {
        inputDimension = 2;    // doesn't handle M currently
    }

#if DEBUG_WKB_READER
    cout << "WKB hasZ: " << hasZ << endl;
#endif

#if DEBUG_WKB_READER
    cout << "WKB dimensions: " << inputDimension << endl;
#endif

    bool hasSRID = ((typeInt & 0x20000000) != 0);

#if DEBUG_WKB_READER
    cout << "WKB hasSRID: " << hasSRID << endl;
#endif

    int SRID = 0;
    if(hasSRID) {
        SRID = dis.readInt();    // read SRID
    }

    // allocate space for ordValues
    if(ordValues.size() < inputDimension) {
        ordValues.resize(inputDimension);
    }

    std::unique_ptr<Geometry> result;

    switch(geometryType) {
    case WKBConstants::wkbPoint :
        result = readPoint();
        break;
    case WKBConstants::wkbLineString :
        result = readLineString();
        break;
    case WKBConstants::wkbPolygon :
        result = readPolygon();
        break;
    case WKBConstants::wkbMultiPoint :
        result = readMultiPoint();
        break;
    case WKBConstants::wkbMultiLineString :
        result = readMultiLineString();
        break;
    case WKBConstants::wkbMultiPolygon :
        result = readMultiPolygon();
        break;
    case WKBConstants::wkbGeometryCollection :
        result = readGeometryCollection();
        break;
    default:
        stringstream err;
        err << "Unknown WKB type " << geometryType;
        throw  ParseException(err.str());
    }

    result->setSRID(SRID);
    return result;
}

std::unique_ptr<Point>
WKBReader::readPoint()
{
    readCoordinate();
    if(inputDimension == 3) {
        return std::unique_ptr<Point>(factory.createPoint(Coordinate(ordValues[0], ordValues[1], ordValues[2])));
    }
    else {
        return std::unique_ptr<Point>(factory.createPoint(Coordinate(ordValues[0], ordValues[1])));
    }
}

std::unique_ptr<LineString>
WKBReader::readLineString()
{
    int size = dis.readInt();
#if DEBUG_WKB_READER
    cout << "WKB npoints: " << size << endl;
#endif
    auto pts = readCoordinateSequence(size);
    return factory.createLineString(std::move(pts));
}

std::unique_ptr<LinearRing>
WKBReader::readLinearRing()
{
    int size = dis.readInt();
#if DEBUG_WKB_READER
    cout << "WKB npoints: " << size << endl;
#endif
    auto pts = readCoordinateSequence(size);
    return factory.createLinearRing(std::move(pts));
}

std::unique_ptr<Polygon>
WKBReader::readPolygon()
{
    int numRings = dis.readInt();

#if DEBUG_WKB_READER
    cout << "WKB numRings: " << numRings << endl;
#endif

    std::unique_ptr<LinearRing> shell;
    if(numRings > 0) {
        shell = readLinearRing();
    }

    if(numRings > 1) {
        std::vector<std::unique_ptr<LinearRing>> holes(numRings - 1);
        for(int i = 0; i < numRings - 1; i++) {
            holes[i] = readLinearRing();
        }

        return factory.createPolygon(std::move(shell), std::move(holes));
    }
    return factory.createPolygon(std::move(shell));
}

std::unique_ptr<MultiPoint>
WKBReader::readMultiPoint()
{
    int numGeoms = dis.readInt();
    std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);

    for(int i = 0; i < numGeoms; i++) {
        geoms[i] = readGeometry();
        if(!dynamic_cast<Point*>(geoms[i].get())) {
            stringstream err;
            err << BAD_GEOM_TYPE_MSG << " MultiPoint";
            throw ParseException(err.str());
        }
    }

    return factory.createMultiPoint(std::move(geoms));
}

std::unique_ptr<MultiLineString>
WKBReader::readMultiLineString()
{
    int numGeoms = dis.readInt();
    std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);

    for(int i = 0; i < numGeoms; i++) {
        geoms[i] = readGeometry();
        if(!dynamic_cast<LineString*>(geoms[i].get())) {
            stringstream err;
            err << BAD_GEOM_TYPE_MSG << " LineString";
            throw  ParseException(err.str());
        }
    }

    return factory.createMultiLineString(std::move(geoms));
}

std::unique_ptr<MultiPolygon>
WKBReader::readMultiPolygon()
{
    int numGeoms = dis.readInt();
    std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);

    for(int i = 0; i < numGeoms; i++) {
        geoms[i] = readGeometry();
        if(!dynamic_cast<Polygon*>(geoms[i].get())) {
            stringstream err;
            err << BAD_GEOM_TYPE_MSG << " Polygon";
            throw  ParseException(err.str());
        }
    }

    return factory.createMultiPolygon(std::move(geoms));
}

std::unique_ptr<GeometryCollection>
WKBReader::readGeometryCollection()
{
    int numGeoms = dis.readInt();
    std::vector<std::unique_ptr<Geometry>> geoms(numGeoms);

    for(int i = 0; i < numGeoms; i++) {
        geoms[i] = readGeometry();
    }

    return factory.createGeometryCollection(std::move(geoms));
}

std::unique_ptr<CoordinateSequence>
WKBReader::readCoordinateSequence(int size)
{
    auto seq = factory.getCoordinateSequenceFactory()->create(size, inputDimension);
    auto targetDim = seq->getDimension();
    if(targetDim > inputDimension) {
        targetDim = inputDimension;
    }
    for(int i = 0; i < size; i++) {
        readCoordinate();
        for(unsigned int j = 0; j < targetDim; j++) {
            seq->setOrdinate(i, j, ordValues[j]);
        }
    }
    return seq;
}

void
WKBReader::readCoordinate()
{
    const PrecisionModel& pm = *factory.getPrecisionModel();
    for(unsigned int i = 0; i < inputDimension; ++i) {
        if(i <= 1) {
            ordValues[i] = pm.makePrecise(dis.readDouble());
        }
        else {
            ordValues[i] = dis.readDouble();
        }
    }
#if DEBUG_WKB_READER
    cout << "WKB coordinate: " << ordValues[0] << "," << ordValues[1] << endl;
#endif
}

} // namespace geos.io
} // namespace geos
<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
#include <windows.h>
#endif
#include <string>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include "util/exception.h"
#include "util/sstream.h"
#include "util/name.h"
#include "util/optional.h"
#include "util/realpath.h"
#include "util/lean_path.h"

#ifndef LEAN_DEFAULT_MODULE_FILE_NAME
#define LEAN_DEFAULT_MODULE_FILE_NAME "default"
#endif

namespace lean {
file_not_found_exception::file_not_found_exception(std::string const & fname):
    exception(sstream() << "file '" << fname << "' not found in the LEAN_PATH"),
    m_fname(fname) {}

static std::string g_default_file_name(LEAN_DEFAULT_MODULE_FILE_NAME);

bool is_directory(char const * pathname) {
    struct stat info;
    if (stat(pathname, &info) != 0 )
        return false; // failed to access pathname
    return info.st_mode & S_IFDIR;
}

#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
// Windows version
static char g_path_sep     = ';';
static char g_sep          = '\\';
static char g_bad_sep      = '/';
static std::string get_exe_location() {
    HMODULE hModule = GetModuleHandleW(NULL);
    WCHAR path[MAX_PATH];
    GetModuleFileNameW(hModule, path, MAX_PATH);
    std::wstring pathstr(path);
    return std::string(pathstr.begin(), pathstr.end());
}
#elif defined(__APPLE__)
// OSX version
#include <mach-o/dyld.h>
#include <limits.h>
#include <stdlib.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char buf1[PATH_MAX];
    char buf2[PATH_MAX];
    uint32_t bufsize = PATH_MAX;
    if (_NSGetExecutablePath(buf1, &bufsize) != 0)
        throw exception("failed to locate Lean executable location");
    if (!realpath(buf1, buf2))
        throw exception("failed to resolve symbolic links in " + std::string(buf1));
    return std::string(buf2);
}
#else
// Linux version
#include <unistd.h>
#include <string.h>
#include <limits.h> // NOLINT
#include <stdio.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char path[PATH_MAX];
    char dest[PATH_MAX];
    memset(dest, 0, PATH_MAX);
    pid_t pid = getpid();
    snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
    if (readlink(path, dest, PATH_MAX) == -1) {
        throw exception("failed to locate Lean executable location");
    } else {
        return std::string(dest);
    }
}
#endif

std::string normalize_path(std::string f) {
    for (auto & c : f) {
        if (c == g_bad_sep)
            c = g_sep;
    }
    return f;
}

std::string get_path(std::string f) {
    while (true) {
        if (f.empty())
            throw exception("failed to locate Lean executable location");
        if (f.back() == g_sep) {
            f.pop_back();
            return f;
        }
        f.pop_back();
    }
}

static std::string              g_lean_path;
static std::vector<std::string> g_lean_path_vector;
void init_lean_path() {
    char * r = getenv("LEAN_PATH");
    if (r == nullptr) {
        g_lean_path  = ".";
        std::string exe_path = get_path(get_exe_location());
        g_lean_path += g_path_sep;
        g_lean_path += exe_path + g_sep + ".." + g_sep + "library";
    } else {
        g_lean_path = r;
    }
    g_lean_path_vector.clear();
    g_lean_path = normalize_path(g_lean_path);
    unsigned i  = 0;
    unsigned j  = 0;
    unsigned sz = g_lean_path.size();
    for (; j < sz; j++) {
        if (g_lean_path[j] == g_path_sep) {
            if (j > i)
                g_lean_path_vector.push_back(g_lean_path.substr(i, j - i));
            i = j + 1;
        }
    }
    if (j > i)
        g_lean_path_vector.push_back(g_lean_path.substr(i, j - i));
}

struct init_lean_path_fn {
    init_lean_path_fn() { init_lean_path(); }
};
static init_lean_path_fn g_init_lean_path_fn;
static std::string       g_sep_str(1, g_sep);

bool has_file_ext(std::string const & fname, char const * ext) {
    unsigned ext_len = strlen(ext);
    return fname.size() > ext_len && fname.substr(fname.size() - ext_len, ext_len) == ext;
}

bool is_lean_file(std::string const & fname) {
    return has_file_ext(fname, ".lean");
}

bool is_olean_file(std::string const & fname) {
    return has_file_ext(fname, ".olean");
}

bool is_lua_file(std::string const & fname) {
    return has_file_ext(fname, ".lua");
}

bool is_known_file_ext(std::string const & fname) {
    return is_lean_file(fname) || is_olean_file(fname) || is_lua_file(fname);
}

optional<std::string> check_file_core(std::string file, char const * ext) {
    if (ext)
        file += ext;
    std::ifstream ifile(file);
    if (ifile)
        return optional<std::string>(lrealpath(file.c_str()));
    else
        return optional<std::string>();
}

optional<std::string> check_file(std::string const & path, std::string const & fname, char const * ext = nullptr) {
    std::string file = path + g_sep + fname;
    if (is_directory(file.c_str())) {
        std::string default_file = file + g_sep + g_default_file_name;
        if (auto r1 = check_file_core(default_file, ext)) {
            if (auto r2 = check_file_core(file, ext))
                throw exception(sstream() << "ambiguous import, it can be '" << *r1 << "' or '" << *r2 << "'");
            return r1;
        }
    }
    return check_file_core(file, ext);
}

std::string name_to_file(name const & fname) {
    return fname.to_string(g_sep_str.c_str());
}

std::string find_file(std::string fname, std::initializer_list<char const *> const & extensions) {
    bool is_known = is_known_file_ext(fname);
    fname = normalize_path(fname);
    for (auto path : g_lean_path_vector) {
        if (is_known) {
            if (auto r = check_file(path, fname))
                return *r;
        } else {
            for (auto ext : extensions) {
                if (auto r = check_file(path, fname, ext))
                    return *r;
            }
        }
    }
    throw file_not_found_exception(fname);
}

std::string find_file(std::string const & base, optional<unsigned> const & rel, name const & fname,
                      std::initializer_list<char const *> const & extensions) {
    if (!rel) {
        return find_file(fname.to_string(g_sep_str.c_str()), extensions);
    } else {
        auto path = base;
        for (unsigned i = 0; i < *rel; i++) {
            path += g_sep;
            path += "..";
        }
        for (auto ext : extensions) {
            if (auto r = check_file(path, fname.to_string(g_sep_str.c_str()), ext))
                return *r;
        }
        throw file_not_found_exception(fname.to_string());
    }
}

std::string find_file(std::string const & base, optional<unsigned> const & k, name const & fname, char const * ext) {
    return find_file(base, k, fname, {ext});
}

std::string find_file(std::string fname) {
    return find_file(fname, {".olean", ".lean", ".lua"});
}

std::string find_file(name const & fname) {
    return find_file(fname.to_string(g_sep_str.c_str()));
}

std::string find_file(name const & fname, std::initializer_list<char const *> const & exts) {
    return find_file(fname.to_string(g_sep_str.c_str()), exts);
}

char const * get_lean_path() {
    return g_lean_path.c_str();
}

void display_path(std::ostream & out, std::string const & fname) {
    out << fname;
}

std::string dirname(char const * fname) {
    if (fname == nullptr)
        return ".";
    unsigned i        = 0;
    unsigned last_sep = 0;
    bool found_sep    = false;
    char const * it   = fname;
    while (*it) {
        if (*it == g_sep) {
            found_sep = true;
            last_sep  = i;
        }
        ++i;
        ++it;
    }
    if (!found_sep) {
        return ".";
    } else {
        std::string r;
        for (unsigned i = 0; i < last_sep; i++)
            r.push_back(fname[i]);
        return r;
    }
}

std::string path_append(char const * p1, char const * p2) {
    std::string r(p1);
    r += g_sep;
    r += p2;
    return r;
}
}
<commit_msg>feat(util/lean_path): normalize path in function dirname<commit_after>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
#include <windows.h>
#endif
#include <string>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include "util/exception.h"
#include "util/sstream.h"
#include "util/name.h"
#include "util/optional.h"
#include "util/realpath.h"
#include "util/lean_path.h"

#ifndef LEAN_DEFAULT_MODULE_FILE_NAME
#define LEAN_DEFAULT_MODULE_FILE_NAME "default"
#endif

namespace lean {
file_not_found_exception::file_not_found_exception(std::string const & fname):
    exception(sstream() << "file '" << fname << "' not found in the LEAN_PATH"),
    m_fname(fname) {}

static std::string g_default_file_name(LEAN_DEFAULT_MODULE_FILE_NAME);

bool is_directory(char const * pathname) {
    struct stat info;
    if (stat(pathname, &info) != 0 )
        return false; // failed to access pathname
    return info.st_mode & S_IFDIR;
}

#if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN)
// Windows version
static char g_path_sep     = ';';
static char g_sep          = '\\';
static char g_bad_sep      = '/';
static std::string get_exe_location() {
    HMODULE hModule = GetModuleHandleW(NULL);
    WCHAR path[MAX_PATH];
    GetModuleFileNameW(hModule, path, MAX_PATH);
    std::wstring pathstr(path);
    return std::string(pathstr.begin(), pathstr.end());
}
#elif defined(__APPLE__)
// OSX version
#include <mach-o/dyld.h>
#include <limits.h>
#include <stdlib.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char buf1[PATH_MAX];
    char buf2[PATH_MAX];
    uint32_t bufsize = PATH_MAX;
    if (_NSGetExecutablePath(buf1, &bufsize) != 0)
        throw exception("failed to locate Lean executable location");
    if (!realpath(buf1, buf2))
        throw exception("failed to resolve symbolic links in " + std::string(buf1));
    return std::string(buf2);
}
#else
// Linux version
#include <unistd.h>
#include <string.h>
#include <limits.h> // NOLINT
#include <stdio.h>
static char g_path_sep     = ':';
static char g_sep          = '/';
static char g_bad_sep      = '\\';
static std::string get_exe_location() {
    char path[PATH_MAX];
    char dest[PATH_MAX];
    memset(dest, 0, PATH_MAX);
    pid_t pid = getpid();
    snprintf(path, PATH_MAX, "/proc/%d/exe", pid);
    if (readlink(path, dest, PATH_MAX) == -1) {
        throw exception("failed to locate Lean executable location");
    } else {
        return std::string(dest);
    }
}
#endif

std::string normalize_path(std::string f) {
    for (auto & c : f) {
        if (c == g_bad_sep)
            c = g_sep;
    }
    return f;
}

std::string get_path(std::string f) {
    while (true) {
        if (f.empty())
            throw exception("failed to locate Lean executable location");
        if (f.back() == g_sep) {
            f.pop_back();
            return f;
        }
        f.pop_back();
    }
}

static std::string              g_lean_path;
static std::vector<std::string> g_lean_path_vector;
void init_lean_path() {
    char * r = getenv("LEAN_PATH");
    if (r == nullptr) {
        g_lean_path  = ".";
        std::string exe_path = get_path(get_exe_location());
        g_lean_path += g_path_sep;
        g_lean_path += exe_path + g_sep + ".." + g_sep + "library";
    } else {
        g_lean_path = r;
    }
    g_lean_path_vector.clear();
    g_lean_path = normalize_path(g_lean_path);
    unsigned i  = 0;
    unsigned j  = 0;
    unsigned sz = g_lean_path.size();
    for (; j < sz; j++) {
        if (g_lean_path[j] == g_path_sep) {
            if (j > i)
                g_lean_path_vector.push_back(g_lean_path.substr(i, j - i));
            i = j + 1;
        }
    }
    if (j > i)
        g_lean_path_vector.push_back(g_lean_path.substr(i, j - i));
}

struct init_lean_path_fn {
    init_lean_path_fn() { init_lean_path(); }
};
static init_lean_path_fn g_init_lean_path_fn;
static std::string       g_sep_str(1, g_sep);

bool has_file_ext(std::string const & fname, char const * ext) {
    unsigned ext_len = strlen(ext);
    return fname.size() > ext_len && fname.substr(fname.size() - ext_len, ext_len) == ext;
}

bool is_lean_file(std::string const & fname) {
    return has_file_ext(fname, ".lean");
}

bool is_olean_file(std::string const & fname) {
    return has_file_ext(fname, ".olean");
}

bool is_lua_file(std::string const & fname) {
    return has_file_ext(fname, ".lua");
}

bool is_known_file_ext(std::string const & fname) {
    return is_lean_file(fname) || is_olean_file(fname) || is_lua_file(fname);
}

optional<std::string> check_file_core(std::string file, char const * ext) {
    if (ext)
        file += ext;
    std::ifstream ifile(file);
    if (ifile)
        return optional<std::string>(lrealpath(file.c_str()));
    else
        return optional<std::string>();
}

optional<std::string> check_file(std::string const & path, std::string const & fname, char const * ext = nullptr) {
    std::string file = path + g_sep + fname;
    if (is_directory(file.c_str())) {
        std::string default_file = file + g_sep + g_default_file_name;
        if (auto r1 = check_file_core(default_file, ext)) {
            if (auto r2 = check_file_core(file, ext))
                throw exception(sstream() << "ambiguous import, it can be '" << *r1 << "' or '" << *r2 << "'");
            return r1;
        }
    }
    return check_file_core(file, ext);
}

std::string name_to_file(name const & fname) {
    return fname.to_string(g_sep_str.c_str());
}

std::string find_file(std::string fname, std::initializer_list<char const *> const & extensions) {
    bool is_known = is_known_file_ext(fname);
    fname = normalize_path(fname);
    for (auto path : g_lean_path_vector) {
        if (is_known) {
            if (auto r = check_file(path, fname))
                return *r;
        } else {
            for (auto ext : extensions) {
                if (auto r = check_file(path, fname, ext))
                    return *r;
            }
        }
    }
    throw file_not_found_exception(fname);
}

std::string find_file(std::string const & base, optional<unsigned> const & rel, name const & fname,
                      std::initializer_list<char const *> const & extensions) {
    if (!rel) {
        return find_file(fname.to_string(g_sep_str.c_str()), extensions);
    } else {
        auto path = base;
        for (unsigned i = 0; i < *rel; i++) {
            path += g_sep;
            path += "..";
        }
        for (auto ext : extensions) {
            if (auto r = check_file(path, fname.to_string(g_sep_str.c_str()), ext))
                return *r;
        }
        throw file_not_found_exception(fname.to_string());
    }
}

std::string find_file(std::string const & base, optional<unsigned> const & k, name const & fname, char const * ext) {
    return find_file(base, k, fname, {ext});
}

std::string find_file(std::string fname) {
    return find_file(fname, {".olean", ".lean", ".lua"});
}

std::string find_file(name const & fname) {
    return find_file(fname.to_string(g_sep_str.c_str()));
}

std::string find_file(name const & fname, std::initializer_list<char const *> const & exts) {
    return find_file(fname.to_string(g_sep_str.c_str()), exts);
}

char const * get_lean_path() {
    return g_lean_path.c_str();
}

void display_path(std::ostream & out, std::string const & fname) {
    out << fname;
}

std::string dirname(char const * fname) {
    if (fname == nullptr)
        return ".";
    std::string nfname = normalize_path(std::string(fname));
    fname = nfname.c_str();
    unsigned i        = 0;
    unsigned last_sep = 0;
    bool found_sep    = false;
    char const * it   = fname;
    while (*it) {
        if (*it == g_sep) {
            found_sep = true;
            last_sep  = i;
        }
        ++i;
        ++it;
    }
    if (!found_sep) {
        return ".";
    } else {
        std::string r;
        for (unsigned i = 0; i < last_sep; i++)
            r.push_back(fname[i]);
        return r;
    }
}

std::string path_append(char const * p1, char const * p2) {
    std::string r(p1);
    r += g_sep;
    r += p2;
    return r;
}
}
<|endoftext|>


<commit_before>
#include <private/akPGTATrackLoader.h>
#include <private/akPGTATrack.h>
#include <public/schema/track_generated.h>
#include <public/schema/track.fbs.h>
#include <flatbuffers/idl.h>

static PGTATrack* LoadBinaryTrack(const uint8_t* src, const size_t length, PGTATrack* track);
static PGTATrack* LoadAsciiTrack(const char* src, const size_t length, PGTATrack* track);

static const size_t MAX_TRACK_LEN = (1 << 16);

PGTATrack* PGTATrackLoader::LoadTrack(const char* src, const size_t length, PGTATrack* track)
{
    if (!src || length > MAX_TRACK_LEN || !track)
    {
        return nullptr;
    }

    if (PGTASchema::TrackBufferHasIdentifier(src))
    {
        return LoadBinaryTrack(reinterpret_cast<const uint8_t*>(src), length, track);
    }
    else
    {
        return LoadAsciiTrack(src, length, track);
    }
}

static PGTATrack* LoadBinaryTrack(const uint8_t* src, const size_t length, PGTATrack* track)
{
    flatbuffers::Verifier verifier = flatbuffers::Verifier(src, length);
    if (!PGTASchema::VerifyTrackBuffer(verifier))
    {
        return nullptr;
    }

    const PGTASchema::Track* t = PGTASchema::GetTrack(src);
    auto s = t->samples();
    auto sample = s->Get(0);
    sample->startTime();

    return nullptr;
}

static PGTATrack* LoadAsciiTrack(const char* src, const size_t length, PGTATrack* track)
{
    flatbuffers::Parser parser;
    if (!parser.Parse(PGTASchemaHeader::TRACK_FBS) || !parser.Parse(src))
    {
        return nullptr;
    }

    const PGTASchema::Track* t = PGTASchema::GetTrack(parser.builder_.GetBufferPointer());
    auto s = t->samples();
    auto sample = s->Get(0);
    sample->startTime();

    return nullptr;
}
<commit_msg>Code reduction<commit_after>
#include <private/akPGTATrackLoader.h>
#include <private/akPGTATrack.h>
#include <public/schema/track_generated.h>
#include <public/schema/track.fbs.h>
#include <flatbuffers/idl.h>

static PGTATrack* LoadBinaryTrack(const uint8_t* src, const size_t length, PGTATrack* track);
static PGTATrack* LoadAsciiTrack(const char* src, const size_t length, PGTATrack* track);

static const size_t MAX_TRACK_LEN = (1 << 16);

PGTATrack* PGTATrackLoader::LoadTrack(const char* src, const size_t length, PGTATrack* track)
{
    if (!src || length > MAX_TRACK_LEN || !track)
    {
        return nullptr;
    }

    if (PGTASchema::TrackBufferHasIdentifier(src))
    {
        return LoadBinaryTrack(reinterpret_cast<const uint8_t*>(src), length, track);
    }
    else
    {
        return LoadAsciiTrack(src, length, track);
    }
}

static PGTATrack* LoadBinaryTrack(const uint8_t* src, const size_t length, PGTATrack* track)
{
    flatbuffers::Verifier verifier(src, length);
    if (!PGTASchema::VerifyTrackBuffer(verifier))
    {
        return nullptr;
    }

    const PGTASchema::Track* t = PGTASchema::GetTrack(src);
    auto s = t->samples();
    auto sample = s->Get(0);
    sample->startTime();

    return nullptr;
}

static PGTATrack* LoadAsciiTrack(const char* src, const size_t length, PGTATrack* track)
{
    flatbuffers::Parser parser;
    if (!parser.Parse(PGTASchemaHeader::TRACK_FBS) || !parser.Parse(src))
    {
        return nullptr;
    }

    const PGTASchema::Track* t = PGTASchema::GetTrack(parser.builder_.GetBufferPointer());
    auto s = t->samples();
    auto sample = s->Get(0);
    sample->startTime();

    return nullptr;
}
<|endoftext|>


<commit_before>//
// detail/request_write_test.cpp
// -----------------------------
//
// Copyright (c) 2015 Daniel Joos
//
// Distributed under MIT license. (See file LICENSE)
//

#include <iostream>
#include <gtest/gtest.h>
#include <boost/asio/streambuf.hpp>
#include <boost/shared_ptr.hpp>
#include <libkafka_asio/libkafka_asio.h>

using namespace libkafka_asio;
using namespace libkafka_asio::detail;

class RequestWriteTest :
  public ::testing::Test
{
protected:
  virtual void SetUp()
  {
    ResetStream();
  }

  void ResetStream()
  {
    streambuf.reset(new boost::asio::streambuf());
    stream.reset(new std::iostream(streambuf.get()));
  }

  Bytes ReadEverything()
  {
    Bytes result(new Bytes::element_type());
    while (stream->good())
    {
      char c = 0;
      stream->get(c);
      if (stream->good())
      {
        result->push_back(c);
      }
    }
    return result;
  }

  typedef boost::shared_ptr<boost::asio::streambuf> StreamBufType;
  typedef boost::shared_ptr<std::iostream> StreamType;
  StreamBufType streambuf;
  StreamType stream;
};

TEST_F(RequestWriteTest, StringWireSize)
{
  String test_string1 = "Hello World";
  ASSERT_EQ(2 + 11, StringWireSize(test_string1));
  String test_string2 = "";
  ASSERT_EQ(2 + 0, StringWireSize(test_string2));
}

TEST_F(RequestWriteTest, BytesWireSize)
{
  Bytes test_bytes1;
  ASSERT_EQ(4 + 0, BytesWireSize(test_bytes1));
  Bytes test_bytes2(new Bytes::element_type());
  ASSERT_EQ(4 + 0, BytesWireSize(test_bytes2));
  Bytes test_bytes3(new Bytes::element_type(1024, '\1'));
  ASSERT_EQ(4 + 1024, BytesWireSize(test_bytes3));
}

TEST_F(RequestWriteTest, MessageWireSize)
{
  Message test_message;
  test_message.mutable_key().reset(new Bytes::element_type(1024));
  test_message.mutable_value().reset(new Bytes::element_type(2048));
  size_t expected_size =
    4 +  // Crc
      1 +  // Magic Byte
      1 +  // Attributes
      (4 + 1024) + // Key
      (4 + 2048);  // Value
  ASSERT_EQ(expected_size, MessageWireSize(test_message));
}

TEST_F(RequestWriteTest, MessageSetWireSize)
{
  MessageAndOffset test_message;
  test_message.mutable_key().reset(new Bytes::element_type(1024));
  test_message.mutable_value().reset(new Bytes::element_type(2048));
  MessageSet test_message_set;
  test_message_set.push_back(test_message);
  test_message_set.push_back(test_message);
  test_message_set.push_back(test_message);
  // See test above
  size_t expected_message_size = MessageWireSize(test_message);
  // 3x (offset + msg_size + message)
  size_t expected_size = 3 * (8 + 4 + expected_message_size);
  ASSERT_EQ(expected_size, MessageSetWireSize(test_message_set));
}

TEST_F(RequestWriteTest, RequestMessageWireSize)
{
  MessageAndOffset test_message;
  test_message.mutable_key().reset(new Bytes::element_type(1024));
  test_message.mutable_value().reset(new Bytes::element_type(2048));
  // See test above
  size_t expected_message_size = MessageWireSize(test_message);

  { // Metadata Request
    MetadataRequest req;
    req.AddTopicName("foo");
    req.AddTopicName("bar!");
    size_t expected_size =
      4 +        // Array size
        (2 + 3) +  // 'foo'
        (2 + 4);   // 'bar'
    ASSERT_EQ(expected_size, RequestMessageWireSize(req));
  }
  { // Produce Request
    ProduceRequest req;
    req.AddMessage(test_message, "bar!");
    size_t expected_size =
      2 +  // RequiredAcks
        4 +  // Timeout
        4 +  // Topic Array Size
        (2 + 4) +  // 'bar!'
        4 +  // Partition Array Size
        4 +  // Partition
        4 +  // MessageSetSize
        (8 + 4 + expected_message_size);  // MessageSet
    ASSERT_EQ(expected_size, RequestMessageWireSize(req));
  }
  { // Fetch Request
    FetchRequest req;
    req.FetchTopic("foo");
    size_t expected_size =
      4 +  // ReplicaId
        4 +  // MaxWaitTime
        4 +  // MinBytes
        4 +  // Topic Array Size
        (2 + 3) +  // 'foo'
        4 +  // Partition Array Size
        4 +  // Partition
        8 +  // Fetch Offset
        4;   // MaxBytes
    ASSERT_EQ(expected_size, RequestMessageWireSize(req));
  }
  { // Offset Request
    OffsetRequest req;
    req.FetchTopicOffset("foo", 0, 2500);
    size_t expected_size =
      4 +  // ReplicaId
        4 +  // Topic Array Size
        (2 + 3) +  // 'foo'
        4 +  // Partition Array Size
        4 +  // Partition
        8 +  // Time
        4;   // MaxNumberOfOffsets
    ASSERT_EQ(expected_size, RequestMessageWireSize(req));
  }
}

TEST_F(RequestWriteTest, WriteInt8)
{
  {
    ResetStream();
    WriteInt8(0, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(1, bytes->size());
    ASSERT_EQ(0x00, bytes->front());
  }
  {  // Big-endian
    ResetStream();
    WriteInt8(-1, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(1, bytes->size());
    ASSERT_EQ(0xff, bytes->front());
  }
}

TEST_F(RequestWriteTest, WriteInt16)
{
  {
    ResetStream();
    WriteInt16(0, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(2, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
  }
  {
    ResetStream();
    WriteInt16(-1, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(2, bytes->size());
    ASSERT_EQ(0xff, bytes->at(0));
    ASSERT_EQ(0xff, bytes->at(1));
  }
  {  // Big-endian
    ResetStream();
    WriteInt16(256, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(2, bytes->size());
    ASSERT_EQ(0x01, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
  }
}

TEST_F(RequestWriteTest, WriteInt32)
{
  {
    ResetStream();
    WriteInt32(0, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(4, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
    ASSERT_EQ(0x00, bytes->at(2));
    ASSERT_EQ(0x00, bytes->at(3));
  }
  {
    ResetStream();
    WriteInt32(-1, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(4, bytes->size());
    ASSERT_EQ(0xff, bytes->at(0));
    ASSERT_EQ(0xff, bytes->at(1));
    ASSERT_EQ(0xff, bytes->at(2));
    ASSERT_EQ(0xff, bytes->at(3));
  }
  {  // Big-endian
    ResetStream();
    WriteInt32(65536, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(4, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x01, bytes->at(1));
    ASSERT_EQ(0x00, bytes->at(2));
    ASSERT_EQ(0x00, bytes->at(3));
  }
}

TEST_F(RequestWriteTest, WriteInt64)
{
  {
    ResetStream();
    WriteInt64(0, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(8, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
    ASSERT_EQ(0x00, bytes->at(2));
    ASSERT_EQ(0x00, bytes->at(3));
    ASSERT_EQ(0x00, bytes->at(4));
    ASSERT_EQ(0x00, bytes->at(5));
    ASSERT_EQ(0x00, bytes->at(6));
    ASSERT_EQ(0x00, bytes->at(7));
  }
  {
    ResetStream();
    WriteInt64(-1, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(8, bytes->size());
    ASSERT_EQ(0xff, bytes->at(0));
    ASSERT_EQ(0xff, bytes->at(1));
    ASSERT_EQ(0xff, bytes->at(2));
    ASSERT_EQ(0xff, bytes->at(3));
    ASSERT_EQ(0xff, bytes->at(4));
    ASSERT_EQ(0xff, bytes->at(5));
    ASSERT_EQ(0xff, bytes->at(6));
    ASSERT_EQ(0xff, bytes->at(7));
  }
  {  // Big-endian
    ResetStream();
    WriteInt64(4294967296, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(8, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
    ASSERT_EQ(0x00, bytes->at(2));
    ASSERT_EQ(0x01, bytes->at(3));
    ASSERT_EQ(0x00, bytes->at(4));
    ASSERT_EQ(0x00, bytes->at(5));
    ASSERT_EQ(0x00, bytes->at(6));
    ASSERT_EQ(0x00, bytes->at(7));
  }
}

TEST_F(RequestWriteTest, WriteString)
{
  String test_string = "foo";
  WriteString(test_string, *stream);
  Bytes bytes = ReadEverything();
  ASSERT_TRUE(bytes);
  ASSERT_EQ(2 + 3, bytes->size());
  ASSERT_EQ(0, bytes->at(0));
  ASSERT_EQ(3, bytes->at(1));
  ASSERT_EQ('f', bytes->at(2));
  ASSERT_EQ('o', bytes->at(3));
  ASSERT_EQ('o', bytes->at(4));
}

TEST_F(RequestWriteTest, WriteBytes)
{
  Bytes test_bytes(new Bytes::element_type(3, 0xfe));
  WriteBytes(test_bytes, *stream);
  Bytes bytes = ReadEverything();
  ASSERT_TRUE(bytes);
  ASSERT_EQ(4 + 3, bytes->size());
  ASSERT_EQ(0, bytes->at(0));
  ASSERT_EQ(0, bytes->at(1));
  ASSERT_EQ(0, bytes->at(2));
  ASSERT_EQ(3, bytes->at(3));
  ASSERT_EQ(0xfe, bytes->at(4));
  ASSERT_EQ(0xfe, bytes->at(5));
  ASSERT_EQ(0xfe, bytes->at(6));
}
<commit_msg>Fixed test<commit_after>//
// detail/request_write_test.cpp
// -----------------------------
//
// Copyright (c) 2015 Daniel Joos
//
// Distributed under MIT license. (See file LICENSE)
//

#include <iostream>
#include <gtest/gtest.h>
#include <boost/asio/streambuf.hpp>
#include <boost/shared_ptr.hpp>
#include <libkafka_asio/libkafka_asio.h>

using namespace libkafka_asio;
using namespace libkafka_asio::detail;

class RequestWriteTest :
  public ::testing::Test
{
protected:
  virtual void SetUp()
  {
    ResetStream();
  }

  void ResetStream()
  {
    streambuf.reset(new boost::asio::streambuf());
    stream.reset(new std::iostream(streambuf.get()));
  }

  Bytes ReadEverything()
  {
    Bytes result(new Bytes::element_type());
    while (stream->good())
    {
      char c = 0;
      stream->get(c);
      if (stream->good())
      {
        result->push_back(c);
      }
    }
    return result;
  }

  typedef boost::shared_ptr<boost::asio::streambuf> StreamBufType;
  typedef boost::shared_ptr<std::iostream> StreamType;
  StreamBufType streambuf;
  StreamType stream;
};

TEST_F(RequestWriteTest, StringWireSize)
{
  String test_string1 = "Hello World";
  ASSERT_EQ(2 + 11, StringWireSize(test_string1));
  String test_string2 = "";
  ASSERT_EQ(2 + 0, StringWireSize(test_string2));
}

TEST_F(RequestWriteTest, BytesWireSize)
{
  Bytes test_bytes1;
  ASSERT_EQ(4 + 0, BytesWireSize(test_bytes1));
  Bytes test_bytes2(new Bytes::element_type());
  ASSERT_EQ(4 + 0, BytesWireSize(test_bytes2));
  Bytes test_bytes3(new Bytes::element_type(1024, '\1'));
  ASSERT_EQ(4 + 1024, BytesWireSize(test_bytes3));
}

TEST_F(RequestWriteTest, MessageWireSize)
{
  Message test_message;
  test_message.mutable_key().reset(new Bytes::element_type(1024));
  test_message.mutable_value().reset(new Bytes::element_type(2048));
  size_t expected_size =
    4 +  // Crc
      1 +  // Magic Byte
      1 +  // Attributes
      (4 + 1024) + // Key
      (4 + 2048);  // Value
  ASSERT_EQ(expected_size, MessageWireSize(test_message));
}

TEST_F(RequestWriteTest, MessageSetWireSize)
{
  MessageAndOffset test_message;
  test_message.mutable_key().reset(new Bytes::element_type(1024));
  test_message.mutable_value().reset(new Bytes::element_type(2048));
  MessageSet test_message_set;
  test_message_set.push_back(test_message);
  test_message_set.push_back(test_message);
  test_message_set.push_back(test_message);
  // See test above
  size_t expected_message_size = MessageWireSize(test_message);
  // 3x (offset + msg_size + message)
  size_t expected_size = 3 * (8 + 4 + expected_message_size);
  ASSERT_EQ(expected_size, MessageSetWireSize(test_message_set));
}

TEST_F(RequestWriteTest, RequestMessageWireSize)
{
  MessageAndOffset test_message;
  test_message.mutable_key().reset(new Bytes::element_type(1024));
  test_message.mutable_value().reset(new Bytes::element_type(2048));
  // See test above
  size_t expected_message_size = MessageWireSize(test_message);

  { // Metadata Request
    MetadataRequest req;
    req.AddTopicName("foo");
    req.AddTopicName("bar!");
    size_t expected_size =
      4 +        // Array size
        (2 + 3) +  // 'foo'
        (2 + 4);   // 'bar'
    ASSERT_EQ(expected_size, RequestMessageWireSize(req));
  }
  { // Produce Request
    ProduceRequest req;
    req.AddMessage(test_message, "bar!");
    size_t expected_size =
      2 +  // RequiredAcks
        4 +  // Timeout
        4 +  // Topic Array Size
        (2 + 4) +  // 'bar!'
        4 +  // Partition Array Size
        4 +  // Partition
        4 +  // MessageSetSize
        (8 + 4 + expected_message_size);  // MessageSet
    ASSERT_EQ(expected_size, RequestMessageWireSize(req));
  }
  { // Fetch Request
    FetchRequest req;
    req.FetchTopic("foo", 0);
    size_t expected_size =
      4 +  // ReplicaId
        4 +  // MaxWaitTime
        4 +  // MinBytes
        4 +  // Topic Array Size
        (2 + 3) +  // 'foo'
        4 +  // Partition Array Size
        4 +  // Partition
        8 +  // Fetch Offset
        4;   // MaxBytes
    ASSERT_EQ(expected_size, RequestMessageWireSize(req));
  }
  { // Offset Request
    OffsetRequest req;
    req.FetchTopicOffset("foo", 0, 2500);
    size_t expected_size =
      4 +  // ReplicaId
        4 +  // Topic Array Size
        (2 + 3) +  // 'foo'
        4 +  // Partition Array Size
        4 +  // Partition
        8 +  // Time
        4;   // MaxNumberOfOffsets
    ASSERT_EQ(expected_size, RequestMessageWireSize(req));
  }
}

TEST_F(RequestWriteTest, WriteInt8)
{
  {
    ResetStream();
    WriteInt8(0, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(1, bytes->size());
    ASSERT_EQ(0x00, bytes->front());
  }
  {  // Big-endian
    ResetStream();
    WriteInt8(-1, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(1, bytes->size());
    ASSERT_EQ(0xff, bytes->front());
  }
}

TEST_F(RequestWriteTest, WriteInt16)
{
  {
    ResetStream();
    WriteInt16(0, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(2, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
  }
  {
    ResetStream();
    WriteInt16(-1, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(2, bytes->size());
    ASSERT_EQ(0xff, bytes->at(0));
    ASSERT_EQ(0xff, bytes->at(1));
  }
  {  // Big-endian
    ResetStream();
    WriteInt16(256, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(2, bytes->size());
    ASSERT_EQ(0x01, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
  }
}

TEST_F(RequestWriteTest, WriteInt32)
{
  {
    ResetStream();
    WriteInt32(0, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(4, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
    ASSERT_EQ(0x00, bytes->at(2));
    ASSERT_EQ(0x00, bytes->at(3));
  }
  {
    ResetStream();
    WriteInt32(-1, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(4, bytes->size());
    ASSERT_EQ(0xff, bytes->at(0));
    ASSERT_EQ(0xff, bytes->at(1));
    ASSERT_EQ(0xff, bytes->at(2));
    ASSERT_EQ(0xff, bytes->at(3));
  }
  {  // Big-endian
    ResetStream();
    WriteInt32(65536, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(4, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x01, bytes->at(1));
    ASSERT_EQ(0x00, bytes->at(2));
    ASSERT_EQ(0x00, bytes->at(3));
  }
}

TEST_F(RequestWriteTest, WriteInt64)
{
  {
    ResetStream();
    WriteInt64(0, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(8, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
    ASSERT_EQ(0x00, bytes->at(2));
    ASSERT_EQ(0x00, bytes->at(3));
    ASSERT_EQ(0x00, bytes->at(4));
    ASSERT_EQ(0x00, bytes->at(5));
    ASSERT_EQ(0x00, bytes->at(6));
    ASSERT_EQ(0x00, bytes->at(7));
  }
  {
    ResetStream();
    WriteInt64(-1, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(8, bytes->size());
    ASSERT_EQ(0xff, bytes->at(0));
    ASSERT_EQ(0xff, bytes->at(1));
    ASSERT_EQ(0xff, bytes->at(2));
    ASSERT_EQ(0xff, bytes->at(3));
    ASSERT_EQ(0xff, bytes->at(4));
    ASSERT_EQ(0xff, bytes->at(5));
    ASSERT_EQ(0xff, bytes->at(6));
    ASSERT_EQ(0xff, bytes->at(7));
  }
  {  // Big-endian
    ResetStream();
    WriteInt64(4294967296, *stream);
    Bytes bytes = ReadEverything();
    ASSERT_TRUE(bytes);
    ASSERT_EQ(8, bytes->size());
    ASSERT_EQ(0x00, bytes->at(0));
    ASSERT_EQ(0x00, bytes->at(1));
    ASSERT_EQ(0x00, bytes->at(2));
    ASSERT_EQ(0x01, bytes->at(3));
    ASSERT_EQ(0x00, bytes->at(4));
    ASSERT_EQ(0x00, bytes->at(5));
    ASSERT_EQ(0x00, bytes->at(6));
    ASSERT_EQ(0x00, bytes->at(7));
  }
}

TEST_F(RequestWriteTest, WriteString)
{
  String test_string = "foo";
  WriteString(test_string, *stream);
  Bytes bytes = ReadEverything();
  ASSERT_TRUE(bytes);
  ASSERT_EQ(2 + 3, bytes->size());
  ASSERT_EQ(0, bytes->at(0));
  ASSERT_EQ(3, bytes->at(1));
  ASSERT_EQ('f', bytes->at(2));
  ASSERT_EQ('o', bytes->at(3));
  ASSERT_EQ('o', bytes->at(4));
}

TEST_F(RequestWriteTest, WriteBytes)
{
  Bytes test_bytes(new Bytes::element_type(3, 0xfe));
  WriteBytes(test_bytes, *stream);
  Bytes bytes = ReadEverything();
  ASSERT_TRUE(bytes);
  ASSERT_EQ(4 + 3, bytes->size());
  ASSERT_EQ(0, bytes->at(0));
  ASSERT_EQ(0, bytes->at(1));
  ASSERT_EQ(0, bytes->at(2));
  ASSERT_EQ(3, bytes->at(3));
  ASSERT_EQ(0xfe, bytes->at(4));
  ASSERT_EQ(0xfe, bytes->at(5));
  ASSERT_EQ(0xfe, bytes->at(6));
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "prop_tree.h"

#include <stdlib.h>

#include <iostream>
#include <limits>
#include <sstream>

namespace tera {

// these chars are not belong to symbols
bool IsIdentifierChar(const char c) {
    return ((c <= '9' && c >= '0') ||
            (c <= 'z' && c >= 'a') ||
            (c <= 'Z' && c >= 'A') ||
            c == '.' ||
            c == '_' ||
            c == '-');
}

Tokenizer::Tokenizer(const std::string& input)
    : origin_(input), cur_pos_(0) {}

Tokenizer::~Tokenizer() {}

void Tokenizer::ConsumeUselessChars() {
    while (cur_pos_ < origin_.size()) {
        switch (origin_[cur_pos_]) {
        case '\n':
        case '\t':
        case '\v':
        case ' ':
            cur_pos_++;
            continue;
        case '#':
            // reach a line-comment, discard all chars in this line
            while (++cur_pos_ < origin_.size()) {
                if (origin_[cur_pos_] == '\n') {
                    break;
                }
            }
            continue;
        default:
            return;
        }
    }
}

void Tokenizer::ConsumeIdentifier() {
    current_.clear();
    current_.type = IDENTIFIER;
    while (cur_pos_ < origin_.size()) {
        char c = origin_[cur_pos_];
        if (IsIdentifierChar(c)) {
            current_.push_back(c);
            cur_pos_++;
            continue;
        } else {
            break;
        }
    }
}

void Tokenizer::ConsumeSymbol() {
    current_.clear();
    current_.type = SYMBOL;
    current_.push_back(origin_[cur_pos_]);
    cur_pos_++;
}

bool Tokenizer::Next() {
    ConsumeUselessChars();
    if (cur_pos_ >= origin_.size()) {
        return false;
    }
    if (IsIdentifierChar(origin_[cur_pos_])) {
        ConsumeIdentifier();
    } else {
        ConsumeSymbol();
    }
    return true;
}

PropTree::PropTree()
    : root_(NULL),
      max_depth_(0),
      min_depth_(std::numeric_limits<int>::max()) {
}

PropTree::~PropTree() {
    delete root_;
}

void PropTree::Reset() {
    delete root_;
    max_depth_ = 0;
    min_depth_ = std::numeric_limits<int>::max();
    state_.clear();
}

bool PropTree::ParseFromString(const std::string& input) {
    Reset();
    int angle_braket_diff = 0;
    int brace_diff = 0;
    Tokenizer tr(input);
    std::deque<Tokenizer::Token> tokens;
    while (tr.Next()) {
        const Tokenizer::Token& token = tr.current();
        if (token.type == Tokenizer::SYMBOL) {
            if (token.text == "<") {
                angle_braket_diff++;
            } else if (token.text == ">") {
                if (angle_braket_diff <= 0) {
                    AddError("syntax error: \">\" should be after \"<\".");
                }
                angle_braket_diff--;
            } else if (token.text == "{") {
                brace_diff++;
            } else if (token.text == "}") {
                if (brace_diff <= 0) {
                    AddError("syntax error: \"}\" should be after \"{\".");
                }
                brace_diff--;
            }
        }
        tokens.push_back(token);
    }
    if (angle_braket_diff != 0) {
        AddError("syntax error: \"<\" and \">\" are not matching.");
        return false;
    }
    if (brace_diff != 0) {
        AddError("syntax error: \"{\" and \"}\" are not matching.");
        return false;
    }

    return ParseFromTokens(tokens, 1, &root_);
}

bool PropTree::ParseFromTokens(std::deque<Tokenizer::Token>& tokens,
                               int depth, Node** node) {
    if (tokens.size() <= 0) {
        return false;
    }
    if (tokens.front().type != Tokenizer::IDENTIFIER) {
        AddError("syntax error: node name error: " + tokens.front().text);
        return false;
    }
    *node = new Node();
    Node*& node_t = *node;

    // get node name and pop it out from token queue
    node_t->name_ = tokens.front().text;
    tokens.pop_front();

    // get all props and pop them out from token queue
    if (tokens.size() > 0 && tokens.front().text == "<") {
        tokens.pop_front(); // pop "<"
        while (tokens.size() > 3 && tokens.front().text != ">") {
            if (tokens.front().text == ",") {
                // reach a comma, discard it
                tokens.pop_front();
            }
            std::string prop_name = tokens.front().text;
            tokens.pop_front();
            std::string eq = tokens.front().text;
            tokens.pop_front();
            std::string prop_value = tokens.front().text;
            tokens.pop_front();
            if (eq != "=" || prop_name == ">" || prop_value == ">") {
                AddError("syntax error: property format error: "
                         + prop_name + eq + prop_value);
                return false;
            }
            node_t->properties_[prop_name] = prop_value;
        }
        if (tokens.front().text != ">") {
            AddError("syntax error: property format error: " + tokens.front().text);
            return false;
        }
        tokens.pop_front();  // pop ">"
    } else {
        // none property
    }

    // get all children from token queue
    if (tokens.size() > 2) {
        if (tokens.front().text != "{" || tokens.back().text != "}") {
            AddError("syntax error: child node format error: " + node_t->name_
                     + tokens.front().text + " " + tokens.back().text);
            return false;
        }
        tokens.pop_front();  // pop "{"
        tokens.pop_back();  // pop "}"
        std::deque<Tokenizer::Token> child_tokens;
        int is_inner_comma = 0;
        while (tokens.size() > 0) {
            child_tokens.push_back(tokens.front());
            std::string tokentext = tokens.front().text;
            if (tokens.size() == 1 && tokentext == ",") {
                // skip the last ',', e.g. "root{child1, child2, }"
                return true;
            }
            tokens.pop_front();

            if (tokentext == "<" || tokentext == "{") {
                is_inner_comma++;
            } else if (tokentext == ">" || tokentext == "}") {
                is_inner_comma--;
            } else if (tokentext == "," && is_inner_comma == 0) {
                child_tokens.pop_back();  // pop the last ","
                node_t->children_.push_back(new Node);
                node_t->children_.back()->mother_ = node_t;
                if (!ParseFromTokens(child_tokens, depth + 1,
                                     &node_t->children_.back())) {
                    return false;
                }
                child_tokens.clear();
            }
        }
        // parse the last child
        node_t->children_.push_back(new Node);
        node_t->children_.back()->mother_ = node_t;
        if (!ParseFromTokens(child_tokens, depth + 1,
                             &node_t->children_.back())) {
            return false;
        }
    } else if (tokens.size() == 0 || tokens.size() == 2) {
        // none child
    } else {
        // never reach here
        abort();
    }
    if (node_t->children_.size() == 0) {
        // this is a leaf node
        if (depth > max_depth_) {
            max_depth_ = depth;
        }
        if (depth < min_depth_) {
            min_depth_ = depth;
        }
    }
    return true;
}

std::string PropTree::FormatString() {
    std::stringstream ss;
    ss << "\n";
    FormatNode("  ", root_, &ss);
    return ss.str();
}

void PropTree::FormatNode(const std::string& line_prefix, Node* node,
                          std::stringstream* ss) {
    *ss << line_prefix << node->name_;
    size_t prop_num = node->properties_.size();
    if (prop_num > 0) {
        size_t prop_cnt = 0;
        *ss << "<";
        std::map<std::string, std::string>::iterator it = node->properties_.begin();
        for (;it != node->properties_.end(); ++it) {
            *ss << it->first << "=" << it->second;
            if (++prop_cnt < prop_num) {
                *ss << ", ";
            }
        }
        *ss << ">";
    }
    size_t children_num = node->children_.size();
    if (children_num > 0) {
        std::string prefix = line_prefix + "    ";
        *ss << " {\n";
        for (size_t i = 0; i < children_num; ++i) {
            FormatNode(prefix, node->children_[i], ss);
            if (i < children_num - 1) {
                *ss << ",";
            }
            *ss << "\n";
        }
        *ss << line_prefix << "}";
    }
}

void PropTree::AddError(const std::string& error_str) {
    state_.append(error_str + "\n");
}
}  // namespace tera
<commit_msg>prop_tree: allow comma at the end of children list (e.g. root{c1,c2,})<commit_after>// Copyright (c) 2015, Baidu.com, Inc. All Rights Reserved
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "prop_tree.h"

#include <stdlib.h>

#include <iostream>
#include <limits>
#include <sstream>

namespace tera {

// these chars are not belong to symbols
bool IsIdentifierChar(const char c) {
    return ((c <= '9' && c >= '0') ||
            (c <= 'z' && c >= 'a') ||
            (c <= 'Z' && c >= 'A') ||
            c == '.' ||
            c == '_' ||
            c == '-');
}

Tokenizer::Tokenizer(const std::string& input)
    : origin_(input), cur_pos_(0) {}

Tokenizer::~Tokenizer() {}

void Tokenizer::ConsumeUselessChars() {
    while (cur_pos_ < origin_.size()) {
        switch (origin_[cur_pos_]) {
        case '\n':
        case '\t':
        case '\v':
        case ' ':
            cur_pos_++;
            continue;
        case '#':
            // reach a line-comment, discard all chars in this line
            while (++cur_pos_ < origin_.size()) {
                if (origin_[cur_pos_] == '\n') {
                    break;
                }
            }
            continue;
        default:
            return;
        }
    }
}

void Tokenizer::ConsumeIdentifier() {
    current_.clear();
    current_.type = IDENTIFIER;
    while (cur_pos_ < origin_.size()) {
        char c = origin_[cur_pos_];
        if (IsIdentifierChar(c)) {
            current_.push_back(c);
            cur_pos_++;
            continue;
        } else {
            break;
        }
    }
}

void Tokenizer::ConsumeSymbol() {
    current_.clear();
    current_.type = SYMBOL;
    current_.push_back(origin_[cur_pos_]);
    cur_pos_++;
}

bool Tokenizer::Next() {
    ConsumeUselessChars();
    if (cur_pos_ >= origin_.size()) {
        return false;
    }
    if (IsIdentifierChar(origin_[cur_pos_])) {
        ConsumeIdentifier();
    } else {
        ConsumeSymbol();
    }
    return true;
}

PropTree::PropTree()
    : root_(NULL),
      max_depth_(0),
      min_depth_(std::numeric_limits<int>::max()) {
}

PropTree::~PropTree() {
    delete root_;
}

void PropTree::Reset() {
    delete root_;
    max_depth_ = 0;
    min_depth_ = std::numeric_limits<int>::max();
    state_.clear();
}

bool PropTree::ParseFromString(const std::string& input) {
    Reset();
    int angle_braket_diff = 0;
    int brace_diff = 0;
    Tokenizer tr(input);
    std::deque<Tokenizer::Token> tokens;
    while (tr.Next()) {
        const Tokenizer::Token& token = tr.current();
        if (token.type == Tokenizer::SYMBOL) {
            if (token.text == "<") {
                angle_braket_diff++;
            } else if (token.text == ">") {
                if (angle_braket_diff <= 0) {
                    AddError("syntax error: \">\" should be after \"<\".");
                }
                angle_braket_diff--;
            } else if (token.text == "{") {
                brace_diff++;
            } else if (token.text == "}") {
                if (brace_diff <= 0) {
                    AddError("syntax error: \"}\" should be after \"{\".");
                }
                brace_diff--;
            }
        }
        tokens.push_back(token);
    }
    if (angle_braket_diff != 0) {
        AddError("syntax error: \"<\" and \">\" are not matching.");
        return false;
    }
    if (brace_diff != 0) {
        AddError("syntax error: \"{\" and \"}\" are not matching.");
        return false;
    }

    return ParseFromTokens(tokens, 1, &root_);
}

bool PropTree::ParseFromTokens(std::deque<Tokenizer::Token>& tokens,
                               int depth, Node** node) {
    if (tokens.size() == 0) {
        return true;
    }
    if (tokens.front().type != Tokenizer::IDENTIFIER) {
        AddError("syntax error: node name error: " + tokens.front().text);
        return false;
    }
    *node = new Node();
    Node*& node_t = *node;

    // get node name and pop it out from token queue
    node_t->name_ = tokens.front().text;
    tokens.pop_front();

    // get all props and pop them out from token queue
    if (tokens.size() > 0 && tokens.front().text == "<") {
        tokens.pop_front(); // pop "<"
        while (tokens.size() > 3 && tokens.front().text != ">") {
            if (tokens.front().text == ",") {
                // reach a comma, discard it
                tokens.pop_front();
            }
            std::string prop_name = tokens.front().text;
            tokens.pop_front();
            std::string eq = tokens.front().text;
            tokens.pop_front();
            std::string prop_value = tokens.front().text;
            tokens.pop_front();
            if (eq != "=" || prop_name == ">" || prop_value == ">") {
                AddError("syntax error: property format error: "
                         + prop_name + eq + prop_value);
                return false;
            }
            node_t->properties_[prop_name] = prop_value;
        }
        if (tokens.front().text != ">") {
            AddError("syntax error: property format error: " + tokens.front().text);
            return false;
        }
        tokens.pop_front();  // pop ">"
    } else {
        // none property
    }

    // get all children from token queue
    if (tokens.size() > 2) {
        if (tokens.front().text != "{" || tokens.back().text != "}") {
            AddError("syntax error: child node format error: " + node_t->name_
                     + tokens.front().text + " " + tokens.back().text);
            return false;
        }
        tokens.pop_front();  // pop "{"
        tokens.pop_back();  // pop "}"
        if (tokens.back().text == ",") {
            // discard the last ","
            tokens.pop_back();
        }

        std::deque<Tokenizer::Token> child_tokens;
        int is_inner_comma = 0;
        while (tokens.size() > 0) {
            child_tokens.push_back(tokens.front());
            std::string tokentext = tokens.front().text;
            tokens.pop_front();

            if (tokentext == "<" || tokentext == "{") {
                is_inner_comma++;
            } else if (tokentext == ">" || tokentext == "}") {
                is_inner_comma--;
            } else if (tokentext == "," && is_inner_comma == 0) {
                child_tokens.pop_back();  // pop the last ","
                node_t->children_.push_back(new Node);
                node_t->children_.back()->mother_ = node_t;
                if (!ParseFromTokens(child_tokens, depth + 1,
                                     &node_t->children_.back())) {
                    return false;
                }
                child_tokens.clear();
            }
        }
        // parse the last child
        node_t->children_.push_back(new Node);
        node_t->children_.back()->mother_ = node_t;
        if (!ParseFromTokens(child_tokens, depth + 1,
                             &node_t->children_.back())) {
            return false;
        }
    } else if (tokens.size() == 0 || tokens.size() == 2 ||
               (tokens.size() == 1 && tokens.front().text == ",")) {
        // none child
    } else {
        // never reach here
        abort();
    }
    if (node_t->children_.size() == 0) {
        // this is a leaf node
        if (depth > max_depth_) {
            max_depth_ = depth;
        }
        if (depth < min_depth_) {
            min_depth_ = depth;
        }
    }
    return true;
}

std::string PropTree::FormatString() {
    std::stringstream ss;
    ss << "\n";
    FormatNode("  ", root_, &ss);
    return ss.str();
}

void PropTree::FormatNode(const std::string& line_prefix, Node* node,
                          std::stringstream* ss) {
    *ss << line_prefix << node->name_;
    size_t prop_num = node->properties_.size();
    if (prop_num > 0) {
        size_t prop_cnt = 0;
        *ss << "<";
        std::map<std::string, std::string>::iterator it = node->properties_.begin();
        for (;it != node->properties_.end(); ++it) {
            *ss << it->first << "=" << it->second;
            if (++prop_cnt < prop_num) {
                *ss << ", ";
            }
        }
        *ss << ">";
    }
    size_t children_num = node->children_.size();
    if (children_num > 0) {
        std::string prefix = line_prefix + "    ";
        *ss << " {\n";
        for (size_t i = 0; i < children_num; ++i) {
            FormatNode(prefix, node->children_[i], ss);
            if (i < children_num - 1) {
                *ss << ",";
            }
            *ss << "\n";
        }
        *ss << line_prefix << "}";
    }
}

void PropTree::AddError(const std::string& error_str) {
    state_.append(error_str + "\n");
}
}  // namespace tera
<|endoftext|>


<commit_before><commit_msg>L2 RAM procedures.<commit_after><|endoftext|>