@@ -384,7 +384,7 @@ def main(): | |||||
parser.add_argument( | parser.add_argument( | ||||
"--dump-cpp-model", | "--dump-cpp-model", | ||||
help="write a C++ model that can be loaded by " | help="write a C++ model that can be loaded by " | ||||
"megbrain/sdk/load-and-run; " | |||||
"megbrain/lite/load_and_run; " | |||||
"this implies --embed-input", | "this implies --embed-input", | ||||
) | ) | ||||
parser.add_argument( | parser.add_argument( | ||||
@@ -1,4 +0,0 @@ | |||||
/load_and_run | |||||
/data | |||||
/*.gcda | |||||
/*.gcno |
@@ -1,74 +0,0 @@ | |||||
cc_library( | |||||
name = "mgblar", | |||||
copts = ["-std=c++14"], | |||||
srcs = [ | |||||
"src/mgblar.cpp", | |||||
"src/json_loader.cpp", | |||||
"src/text_table.cpp", | |||||
], | |||||
hdrs = [ | |||||
"src/mgblar.h", | |||||
"src/json_loader.h", | |||||
"src/text_table.h", | |||||
"src/npy.h", | |||||
], | |||||
features = if_opt([ | |||||
"no_exceptions", | |||||
"no_rtti", | |||||
]), | |||||
includes = ["src"], | |||||
defines = ["MGB_ENABLE_FASTRUN=1"], | |||||
deps = ["//brain/megbrain:sdk-test"], | |||||
) | |||||
cc_megvii_binary( | |||||
name = "load_and_run", | |||||
copts = ["-std=c++14"], | |||||
srcs = ["main.cpp"], | |||||
features = if_opt([ | |||||
"no_exceptions", | |||||
"no_rtti", | |||||
]), | |||||
internal_deps = [":mgblar"], | |||||
visibility = ["//visibility:public"], | |||||
) | |||||
cc_megvii_shared_object( | |||||
name = "load_and_run_shared", | |||||
copts = ["-std=c++14"], | |||||
srcs = ["main.cpp"], | |||||
features = if_opt([ | |||||
"no_exceptions", | |||||
"no_rtti", | |||||
]), | |||||
internal_deps = [":mgblar"], | |||||
syms = ["main"], | |||||
) | |||||
cc_megvii_binary( | |||||
name = "json_loader_test", | |||||
copts = ["-std=c++14"], | |||||
srcs = ["test/json_loader_test.cpp"], | |||||
internal_deps = [":mgblar"], | |||||
) | |||||
cc_library( | |||||
name = "megbrain_ios_lar_lib", | |||||
srcs = [ | |||||
"src/mgblar.cpp", | |||||
], | |||||
hdrs = [ | |||||
"src/mgblar.h", | |||||
], | |||||
copts = ["-DMGB_NO_MAIN=1"], | |||||
features = if_opt([ | |||||
"no_exceptions", | |||||
"no_rtti", | |||||
]), | |||||
deps = ["//brain/megbrain:sdk-test"], | |||||
) | |||||
cc_megvii_static_library( | |||||
name = "megbrain_ios_lar", | |||||
deps = [":megbrain_ios_lar_lib"], | |||||
) |
@@ -1,24 +0,0 @@ | |||||
include_directories(src) | |||||
file(GLOB_RECURSE SOURCES src/*.cpp main.cpp) | |||||
add_executable(load_and_run ${SOURCES}) | |||||
target_link_libraries(load_and_run megbrain megdnn ${MGE_CUDA_LIBS}) | |||||
# load_and_run_depends_shared always for CI check, please do not delete | |||||
if(BUILD_SHARED_LIBS) | |||||
add_executable(load_and_run_depends_shared ${SOURCES}) | |||||
target_link_libraries(load_and_run_depends_shared megengine) | |||||
if(WIN32 OR MSVC) | |||||
target_compile_definitions(load_and_run_depends_shared PRIVATE MGE_DLL_IMPORT_DATA) | |||||
endif() | |||||
endif() | |||||
install(TARGETS load_and_run EXPORT ${MGE_EXPORT_TARGETS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) | |||||
if(BUILD_SHARED_LIBS) | |||||
install(TARGETS load_and_run_depends_shared EXPORT ${MGE_EXPORT_TARGETS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) | |||||
endif() | |||||
if(MGE_WITH_TEST) | |||||
add_executable(json_loader_test test/json_loader_test.cpp src/json_loader.h src/json_loader.cpp) | |||||
target_link_libraries(json_loader_test megbrain megdnn ${MGE_CUDA_LIBS}) | |||||
endif() |
@@ -1,29 +0,0 @@ | |||||
include ../../Makefile | |||||
MACHINE := $(shell $(MGB_CXX) -dumpmachine) | |||||
ifneq (, $(findstring android, $(MACHINE))) | |||||
DEPS := $(MGB_LIB) ${MEGDNN_LIB} | |||||
CXXFLAGS := $(MGB_CXXFLAGS) -fuse-ld=gold -Isrc | |||||
LDFLAGS := -ldl -llog ${DEPS} | |||||
else | |||||
DEPS := $(MGB_LIB) ${MEGDNN_LIB} | |||||
CXXFLAGS := $(MGB_CXXFLAGS) -fuse-ld=gold -Isrc | |||||
LDFLAGS := -ldl ${DEPS} ${MGB_LDFLAGS} | |||||
endif | |||||
TARGETS := load_and_run | |||||
all: $(TARGETS) | |||||
ifneq (,$(findstring gcov,$(MGB_LDFLAGS))) | |||||
LDFLAGS += --coverage | |||||
endif | |||||
load_and_run: main.cpp src/* $(DEPS) | |||||
$(MGB_CXX) -o $@ main.cpp src/*.cpp $(CXXFLAGS) $(LDFLAGS) | |||||
clean: | |||||
rm -f $(TARGETS) | |||||
.PHONY: all clean |
@@ -1,144 +0,0 @@ | |||||
# Load and Run | |||||
Load a model and run, for testing/debugging/profiling. | |||||
## Build | |||||
<!-- | |||||
--> | |||||
### Build with cmake | |||||
Build MegEngine from source following [README.md](../../README.md). It will also produce the executable, `load_and_run`, which loads a model and runs the test cases attached to the model. | |||||
<!-- | |||||
--> | |||||
## Dump Model with Test Cases Using [dump_with_testcase_mge.py](dump_with_testcase_mge.py) | |||||
### Step 1 | |||||
Dump the model by calling the python API `megengine.jit.trace.dump()`. | |||||
### Step 2 | |||||
Append the test cases to the dumped model using [dump_with_testcase_mge.py](dump_with_testcase_mge.py). | |||||
The basic usage of [dump_with_testcase_mge.py](dump_with_testcase_mge.py) is | |||||
``` | |||||
python3 dump_with_testcase_mge.py model -d input_description -o model_with_testcases | |||||
``` | |||||
where `model` is the file dumped at step 1, `input_description` describes the input data of the test cases, and `model_with_testcases` is the saved model with test cases. | |||||
`input_description` can be provided in the following ways: | |||||
1. In the format `var0:file0;var1:file1...` meaning that `var0` should use | |||||
image file `file0`, `var1` should use image `file1` and so on. If there | |||||
is only one input var, the var name can be omitted. This can be combined | |||||
with `--resize-input` option. | |||||
2. In the format `var0:#rand(min, max, shape...);var1:#rand(min, max)...` | |||||
meaning to fill the corresponding input vars with uniform random numbers | |||||
in the range `[min, max)`, optionally overriding its shape. | |||||
For more usages, run | |||||
``` | |||||
python3 dump_with_testcase_mge.py --help | |||||
``` | |||||
### Example | |||||
1. Obtain the model file by running [xornet.py](../xor-deploy/xornet.py). | |||||
2. Dump the file with test cases attached to the model. | |||||
``` | |||||
python3 dump_with_testcase_mge.py xornet_deploy.mge -o xornet.mge -d "#rand(0.1, 0.8, 4, 2)" | |||||
``` | |||||
3. Verify the correctness by running `load_and_run` at the target platform. | |||||
``` | |||||
load_and_run xornet.mge | |||||
``` | |||||
## `load_and_run --input` the dumped mge file | |||||
You can also use `--input` to set mge file's input, this argument support these 4 formats: | |||||
1. PPM/PGM image file. | |||||
PPM/PGM is supported by OpenCV and simple to parse, you can easily use `cv::imwrite` to generate one. | |||||
``` | |||||
load_and_run model.mge --input "data:image.ppm" | |||||
``` | |||||
`data` is blob name and `image.ppm` is file path, we use `:` to seperate key and value. Please note that `"` is necessary in terminal. | |||||
2. npy file. | |||||
npy is `Numpy` file format, here is a Python example | |||||
``` | |||||
import numpy as np | |||||
import cv2 | |||||
mat = cv2.imread('file.jpg') | |||||
np.save('image.npy', mat) | |||||
arr = np.array([[[1.1, 1.2],[100, 200.0]]], dtype=np.float32) | |||||
np.save('bbox.npy', arr) | |||||
``` | |||||
then `load_and_run` the model | |||||
``` | |||||
load_and_run model.mge --input data:image.npy;bbox.npy | |||||
``` | |||||
3. json format. | |||||
For json format, you have to identify data type and blob shape. Here is a Python example | |||||
``` | |||||
import numpy as np | |||||
import json | |||||
import cv2 | |||||
bbox = np.array([[[1.1, 1.2],[100, 200.0]]], dtype=np.float32) | |||||
obj = dict() | |||||
obj['shape'] = bbox.shape | |||||
obj['raw'] = bbox.flatten().tolist() | |||||
obj['type'] = str(bbox.dtype) | |||||
json_object = dict() | |||||
json_object['bbox'] = obj | |||||
json_str = json.dumps(json_object) | |||||
with open('bbox.json', 'w') as f: | |||||
f.write(json_str) | |||||
f.flush() | |||||
f.close() | |||||
``` | |||||
The json loader in `load_and_run` is not fully implement [RFC7159](https://tools.ietf.org/html/rfc7159), it does not support `boolean` and `utf` string format which is useless during inference. | |||||
Now let's `load-and-run` the model with json file | |||||
``` | |||||
load_and_run model.mge --input data:image.npy:bbox:bbox.json | |||||
``` | |||||
Mutiple key-value pair could be seperated with `;`. | |||||
4. plain string format. | |||||
Also, you can give the value directly | |||||
``` | |||||
load_and_run model.mge --input data:image.ppm --input "bbox:[0,0],[200.0,200.0]" --input "batchid:0" | |||||
``` | |||||
1. `bbox` shape is `[1,2,2]` for `[0,0],[200.0,200.0]`. In order to facilitate user experience, the string parser would add an extra axis for input, thus `bbox:0` is correspond to `[1]` and `bbox:[0]` means that the shape is `[1,1]` | |||||
2. Since we can only identify `int32` and `float32` from this format, don't forget `.` for float number. |
@@ -1,25 +0,0 @@ | |||||
/** | |||||
* \file sdk/load-and-run/main.cpp | |||||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||||
* | |||||
* Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
*/ | |||||
#include "mgblar.h" | |||||
#include "megbrain/common.h" | |||||
int main(int argc, char **argv) { | |||||
MGB_TRY { | |||||
return mgb_load_and_run_main(argc, argv); | |||||
} MGB_CATCH (std::exception &exc, { | |||||
fprintf(stderr, "caught exception: %s\n", exc.what()); | |||||
return -2; | |||||
}) | |||||
} | |||||
// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}} | |||||
@@ -1,299 +0,0 @@ | |||||
/** | |||||
* \file sdk/load-and-run/src/json_loader.cpp | |||||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||||
* | |||||
* Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
* implied. | |||||
*/ | |||||
#include "json_loader.h" | |||||
using namespace mgb; | |||||
template <typename T> | |||||
T* JsonLoader::Value::safe_cast() { | |||||
T* ptr = (T*)(this); | |||||
if (nullptr == ptr) { | |||||
fprintf(stderr, "cast ptr is null\n"); | |||||
} | |||||
return ptr; | |||||
} | |||||
std::unique_ptr<JsonLoader::Value>& JsonLoader::Value::operator[]( | |||||
const std::string& key) { | |||||
mgb_assert(Type::OBJECT == m_type); | |||||
auto t = safe_cast<JsonLoader::ObjectValue>(); | |||||
return t->m_obj.at(key); | |||||
} | |||||
std::unique_ptr<JsonLoader::Value>& JsonLoader::Value::operator[]( | |||||
const size_t index) { | |||||
mgb_assert(Type::ARRAY == m_type); | |||||
auto t = safe_cast<JsonLoader::ArrayValue>(); | |||||
return t->m_obj[index]; | |||||
} | |||||
std::map<std::string, std::unique_ptr<JsonLoader::Value>>& | |||||
JsonLoader::Value::objects() { | |||||
mgb_assert(Type::OBJECT == m_type); | |||||
auto t = safe_cast<JsonLoader::ObjectValue>(); | |||||
return t->m_obj; | |||||
} | |||||
size_t JsonLoader::Value::len() { | |||||
if (Type::ARRAY == m_type) { | |||||
auto t = safe_cast<JsonLoader::ArrayValue>(); | |||||
return t->m_obj.size(); | |||||
} else if (Type::OBJECT == m_type) { | |||||
auto t = safe_cast<JsonLoader::ObjectValue>(); | |||||
return t->m_obj.size(); | |||||
} | |||||
return 0; | |||||
} | |||||
megdnn::SmallVector<std::unique_ptr<JsonLoader::Value>>& | |||||
JsonLoader::Value::array() { | |||||
mgb_assert(Type::ARRAY == m_type); | |||||
auto t = safe_cast<JsonLoader::ArrayValue>(); | |||||
return t->m_obj; | |||||
} | |||||
double JsonLoader::Value::number() { | |||||
mgb_assert(Type::NUMBER == m_type); | |||||
auto t = safe_cast<JsonLoader::NumberValue>(); | |||||
return t->value(); | |||||
} | |||||
std::string JsonLoader::Value::str() { | |||||
if (Type::STRING == m_type) { | |||||
auto t = safe_cast<StringValue>(); | |||||
return t->value(); | |||||
} | |||||
return std::string(); | |||||
} | |||||
void JsonLoader::expect(char c) { | |||||
mgb_assert(c == (*m_buf)); | |||||
m_buf++; | |||||
} | |||||
void JsonLoader::skip_whitespace() { | |||||
const char* p = m_buf; | |||||
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') { | |||||
++p; | |||||
} | |||||
m_buf = p; | |||||
} | |||||
std::unique_ptr<JsonLoader::Value> JsonLoader::parse_object() { | |||||
expect('{'); | |||||
skip_whitespace(); | |||||
std::unique_ptr<JsonLoader::Value> ret; | |||||
JsonLoader::ObjectValue* pObject = new JsonLoader::ObjectValue(); | |||||
if ('}' == *m_buf) { | |||||
m_buf = m_buf + 1; | |||||
ret.reset((JsonLoader::Value*)(pObject)); | |||||
return ret; | |||||
} | |||||
while (true) { | |||||
std::unique_ptr<JsonLoader::Value> key = parse_string(); | |||||
if (m_state != State::OK) { | |||||
return ret; | |||||
} | |||||
skip_whitespace(); | |||||
if (':' != (*m_buf)) { | |||||
m_state = State::MISS_COLON; | |||||
return ret; | |||||
} | |||||
m_buf++; | |||||
skip_whitespace(); | |||||
std::unique_ptr<JsonLoader::Value> pVal = parse_value(); | |||||
if (m_state != State::OK) { | |||||
return ret; | |||||
} | |||||
if (pObject->m_obj.find(pVal->str()) != pObject->m_obj.end()) { | |||||
m_state = State::KEY_NOT_UNIQUE; | |||||
return ret; | |||||
} | |||||
pObject->m_obj.insert(std::make_pair(key->str(), std::move(pVal))); | |||||
skip_whitespace(); | |||||
if (',' == (*m_buf)) { | |||||
m_buf++; | |||||
skip_whitespace(); | |||||
} else if ('}' == (*m_buf)) { | |||||
m_buf++; | |||||
break; | |||||
} else { | |||||
m_state = State::MISS_BRACE; | |||||
break; | |||||
} | |||||
} | |||||
ret.reset((JsonLoader::Value*)(pObject)); | |||||
return ret; | |||||
} | |||||
std::unique_ptr<JsonLoader::Value> JsonLoader::parse_array() { | |||||
expect('['); | |||||
skip_whitespace(); | |||||
std::unique_ptr<JsonLoader::Value> ret; | |||||
JsonLoader::ArrayValue* pArray = new JsonLoader::ArrayValue(); | |||||
if (']' == *m_buf) { | |||||
m_buf = m_buf + 1; | |||||
ret.reset((JsonLoader::Value*)(pArray)); | |||||
return ret; | |||||
} | |||||
while (true) { | |||||
std::unique_ptr<JsonLoader::Value> pVal = parse_value(); | |||||
if (m_state != State::OK) { | |||||
mgb_assert(0, "parse value failed during pase array"); | |||||
return ret; | |||||
} | |||||
pArray->m_obj.emplace_back(pVal.get()); | |||||
pVal.release(); | |||||
skip_whitespace(); | |||||
if (',' == *m_buf) { | |||||
m_buf++; | |||||
skip_whitespace(); | |||||
} else if (']' == *m_buf) { | |||||
m_buf++; | |||||
break; | |||||
} else { | |||||
m_state = State::BAD_ARRAY; | |||||
return ret; | |||||
} | |||||
} | |||||
ret.reset((JsonLoader::Value*)(pArray)); | |||||
return ret; | |||||
} | |||||
std::unique_ptr<JsonLoader::Value> JsonLoader::parse_string() { | |||||
expect('\"'); | |||||
std::unique_ptr<JsonLoader::Value> ret; | |||||
JsonLoader::StringValue* pStr = new JsonLoader::StringValue(); | |||||
const char* p = m_buf; | |||||
while (true) { | |||||
if (*p == '\"') { | |||||
p++; | |||||
break; | |||||
} else { | |||||
pStr->m_value += (*p); | |||||
p++; | |||||
} | |||||
} | |||||
m_buf = p; | |||||
ret.reset((JsonLoader::Value*)(pStr)); | |||||
return ret; | |||||
} | |||||
std::unique_ptr<JsonLoader::Value> JsonLoader::parse_number() { | |||||
const char* p = m_buf; | |||||
auto loop_digit = [this](const char*& p) { | |||||
if (not std::isdigit(*p)) { | |||||
m_state = State::BAD_DIGIT; | |||||
return; | |||||
} | |||||
while (std::isdigit(*p)) { | |||||
p++; | |||||
} | |||||
return; | |||||
}; | |||||
if (*p == '-') | |||||
p++; | |||||
if (*p == '0') | |||||
p++; | |||||
else { | |||||
loop_digit(std::ref(p)); | |||||
} | |||||
if (*p == '.') { | |||||
p++; | |||||
loop_digit(std::ref(p)); | |||||
} | |||||
if (*p == 'e' || *p == 'E') { | |||||
p++; | |||||
if (*p == '+' || *p == '-') | |||||
p++; | |||||
loop_digit(std::ref(p)); | |||||
} | |||||
JsonLoader::NumberValue* pNum = new JsonLoader::NumberValue(); | |||||
pNum->m_value = strtod(m_buf, nullptr); | |||||
m_buf = p; | |||||
std::unique_ptr<JsonLoader::Value> ret; | |||||
ret.reset((JsonLoader::Value*)(pNum)); | |||||
return ret; | |||||
} | |||||
std::unique_ptr<JsonLoader::Value> JsonLoader::parse_value() { | |||||
switch (*m_buf) { | |||||
case '[': | |||||
return parse_array(); | |||||
case '{': | |||||
return parse_object(); | |||||
case '\"': | |||||
return parse_string(); | |||||
case '\0': | |||||
m_state = State::BAD_TYPE; | |||||
break; | |||||
default: | |||||
return parse_number(); | |||||
} | |||||
return nullptr; | |||||
} | |||||
std::unique_ptr<JsonLoader::Value> JsonLoader::load(const char* content, | |||||
const size_t size) { | |||||
m_buf = content; | |||||
skip_whitespace(); | |||||
std::unique_ptr<JsonLoader::Value> value = parse_value(); | |||||
skip_whitespace(); | |||||
if (m_state != State::OK) { | |||||
return nullptr; | |||||
} | |||||
mgb_assert(size == static_cast<size_t>(m_buf - content)); | |||||
return value; | |||||
} | |||||
std::unique_ptr<JsonLoader::Value> JsonLoader::load(const char* path) { | |||||
std::unique_ptr<std::FILE, void (*)(std::FILE*)> fin( | |||||
std::fopen(path, "rb"), [](std::FILE* fp) { std::fclose(fp); }); | |||||
mgb_assert(fin.get(), "failed to open %s: %s", path, strerror(errno)); | |||||
std::fseek(fin.get(), 0, SEEK_END); | |||||
const size_t size = ftell(fin.get()); | |||||
std::fseek(fin.get(), 0, SEEK_SET); | |||||
std::unique_ptr<char> buf(static_cast<char*>(malloc(size))); | |||||
auto nr = std::fread(buf.get(), 1, size, fin.get()); | |||||
mgb_assert(nr == size); | |||||
return load(buf.get(), size); | |||||
} |
@@ -1,184 +0,0 @@ | |||||
/** | |||||
* \file sdk/load-and-run/src/json_loader.h | |||||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||||
* | |||||
* Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
* implied. | |||||
*/ | |||||
#pragma once | |||||
#include <cctype> | |||||
#include <fstream> | |||||
#include <functional> | |||||
#include <iostream> | |||||
#include <map> | |||||
#include <memory> | |||||
#include "megbrain/common.h" | |||||
#include "megdnn/thin/small_vector.h" | |||||
namespace mgb { | |||||
class JsonLoader { | |||||
public: | |||||
class Value { | |||||
protected: | |||||
enum struct Type : uint8_t { UNKNOWN, NUMBER, STRING, OBJECT, ARRAY }; | |||||
Type m_type; | |||||
public: | |||||
template <typename T> | |||||
T* safe_cast(); | |||||
Value() { m_type = Type::UNKNOWN; } | |||||
Value(Type type) : m_type(type) {} | |||||
virtual ~Value() {} | |||||
bool is_array() { return Type::ARRAY == m_type; } | |||||
bool is_object() { return Type::OBJECT == m_type; } | |||||
bool is_number() { return Type::NUMBER == m_type; } | |||||
bool is_str() { return Type::STRING == m_type; } | |||||
std::unique_ptr<Value>& operator[](const std::string& key); | |||||
std::unique_ptr<Value>& operator[](const size_t index); | |||||
std::map<std::string, std::unique_ptr<Value>>& objects(); | |||||
size_t len(); | |||||
megdnn::SmallVector<std::unique_ptr<Value>>& array(); | |||||
double number(); | |||||
std::string str(); | |||||
}; | |||||
void expect(char c); | |||||
void skip_whitespace(); | |||||
std::unique_ptr<Value> parse_object(); | |||||
std::unique_ptr<Value> parse_array(); | |||||
std::unique_ptr<Value> parse_string(); | |||||
std::unique_ptr<Value> parse_number(); | |||||
std::unique_ptr<Value> parse_value(); | |||||
enum struct State : uint8_t { | |||||
OK = 0, | |||||
BAD_TYPE, | |||||
BAD_DIGIT, | |||||
BAD_ARRAY, | |||||
MISS_COLON, | |||||
MISS_BRACE, | |||||
KEY_NOT_UNIQUE | |||||
}; | |||||
JsonLoader() { m_state = State::OK; } | |||||
std::unique_ptr<Value> load(const char* content, const size_t size); | |||||
std::unique_ptr<Value> load(const char* path); | |||||
class NumberValue final : public Value { | |||||
friend std::unique_ptr<Value> JsonLoader::parse_number(); | |||||
double m_value; | |||||
public: | |||||
NumberValue() : Value(Type::NUMBER) {} | |||||
double value() { return m_value; } | |||||
}; | |||||
class StringValue final : public Value { | |||||
std::string m_value; | |||||
public: | |||||
StringValue() : Value(Type::STRING) {} | |||||
std::string value() { return m_value; } | |||||
friend std::unique_ptr<Value> JsonLoader::parse_string(); | |||||
}; | |||||
class ArrayValue final : public Value { | |||||
megdnn::SmallVector<std::unique_ptr<Value>> m_obj; | |||||
public: | |||||
ArrayValue() : Value(Type::ARRAY) {} | |||||
ArrayValue(ArrayValue& arr) : Value(arr) { | |||||
m_obj.clear(); | |||||
for (auto& item : arr.m_obj) { | |||||
m_obj.emplace_back(item.get()); | |||||
item.release(); | |||||
} | |||||
} | |||||
ArrayValue(ArrayValue&& arr) : Value(arr) { | |||||
m_obj.clear(); | |||||
for (auto& item : arr.m_obj) { | |||||
m_obj.emplace_back(item.get()); | |||||
item.release(); | |||||
} | |||||
} | |||||
friend std::unique_ptr<Value> JsonLoader::parse_array(); | |||||
friend std::unique_ptr<JsonLoader::Value>& JsonLoader::Value:: | |||||
operator[](const size_t index); | |||||
friend megdnn::SmallVector<std::unique_ptr<JsonLoader::Value>>& | |||||
JsonLoader::Value::array(); | |||||
friend size_t JsonLoader::Value::len(); | |||||
}; | |||||
class ObjectValue final : public Value { | |||||
std::map<std::string, std::unique_ptr<Value>> m_obj; | |||||
public: | |||||
ObjectValue() : Value(Type::OBJECT) {} | |||||
ObjectValue(ObjectValue& arr) : Value(arr) { | |||||
m_obj.clear(); | |||||
for (auto itra = arr.m_obj.begin(); itra != arr.m_obj.end(); | |||||
++itra) { | |||||
m_obj.emplace( | |||||
std::make_pair(itra->first, std::move(itra->second))); | |||||
} | |||||
} | |||||
ObjectValue(ObjectValue&& arr) : Value(arr) { | |||||
m_obj.clear(); | |||||
for (auto itra = arr.m_obj.begin(); itra != arr.m_obj.end(); | |||||
++itra) { | |||||
m_obj.emplace( | |||||
std::make_pair(itra->first, std::move(itra->second))); | |||||
} | |||||
} | |||||
friend std::unique_ptr<Value> JsonLoader::parse_object(); | |||||
friend std::unique_ptr<JsonLoader::Value>& JsonLoader::Value:: | |||||
operator[](const std::string&); | |||||
friend std::map<std::string, std::unique_ptr<JsonLoader::Value>>& | |||||
JsonLoader::Value::objects(); | |||||
friend size_t JsonLoader::Value::len(); | |||||
}; | |||||
private: | |||||
const char* m_buf; | |||||
State m_state; | |||||
}; | |||||
} // namespace mgb |
@@ -1,22 +0,0 @@ | |||||
/** | |||||
* \file sdk/load-and-run/src/mgblar.h | |||||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||||
* | |||||
* Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
*/ | |||||
#pragma once | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
int mgb_load_and_run_main(int argc, char **argv); | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif | |||||
// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}} |
@@ -1,627 +0,0 @@ | |||||
/* | |||||
Copyright 2017 Leon Merten Lohse | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in | |||||
all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
#ifndef NPY_H | |||||
#define NPY_H | |||||
#include <algorithm> | |||||
#include <complex> | |||||
#include <cstdint> | |||||
#include <cstring> | |||||
#include <fstream> | |||||
#include <iostream> | |||||
#include <regex> | |||||
#include <sstream> | |||||
#include <stdexcept> | |||||
#include <string> | |||||
#include <unordered_map> | |||||
#include <vector> | |||||
namespace npy { | |||||
/* Compile-time test for byte order. | |||||
If your compiler does not define these per default, you may want to define | |||||
one of these constants manually. | |||||
Defaults to little endian order. */ | |||||
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ | |||||
defined(__BIG_ENDIAN__) || defined(__ARMEB__) || \ | |||||
defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || \ | |||||
defined(__MIBSEB) || defined(__MIBSEB__) | |||||
const bool big_endian = true; | |||||
#else | |||||
const bool big_endian = false; | |||||
#endif | |||||
const char magic_string[] = "\x93NUMPY"; | |||||
const size_t magic_string_length = 6; | |||||
const char little_endian_char = '<'; | |||||
const char big_endian_char = '>'; | |||||
const char no_endian_char = '|'; | |||||
constexpr char host_endian_char = | |||||
(big_endian ? big_endian_char : little_endian_char); | |||||
/* npy array length */ | |||||
typedef unsigned long int ndarray_len_t; | |||||
inline void write_magic(std::ostream& ostream, unsigned char v_major = 1, | |||||
unsigned char v_minor = 0) { | |||||
ostream.write(magic_string, magic_string_length); | |||||
ostream.put(v_major); | |||||
ostream.put(v_minor); | |||||
} | |||||
inline void read_magic(std::istream& istream, unsigned char& v_major, | |||||
unsigned char& v_minor) { | |||||
char buf[magic_string_length + 2]; | |||||
istream.read(buf, magic_string_length + 2); | |||||
if (!istream) { | |||||
fprintf(stderr, "io error: failed reading file"); | |||||
} | |||||
if (0 != std::memcmp(buf, magic_string, magic_string_length)) { | |||||
fprintf(stderr, "this file does not have a valid npy format."); | |||||
} | |||||
v_major = buf[magic_string_length]; | |||||
v_minor = buf[magic_string_length + 1]; | |||||
} | |||||
// typestring magic | |||||
struct Typestring { | |||||
private: | |||||
char c_endian; | |||||
char c_type; | |||||
int len; | |||||
public: | |||||
inline std::string str() { | |||||
const size_t max_buflen = 16; | |||||
char buf[max_buflen]; | |||||
std::sprintf(buf, "%c%c%u", c_endian, c_type, len); | |||||
return std::string(buf); | |||||
} | |||||
Typestring(const std::vector<float>&) | |||||
: c_endian{host_endian_char}, c_type{'f'}, len{sizeof(float)} {} | |||||
Typestring(const std::vector<double>&) | |||||
: c_endian{host_endian_char}, c_type{'f'}, len{sizeof(double)} {} | |||||
Typestring(const std::vector<long double>&) | |||||
: c_endian{host_endian_char}, | |||||
c_type{'f'}, | |||||
len{sizeof(long double)} {} | |||||
Typestring(const std::vector<char>&) | |||||
: c_endian{no_endian_char}, c_type{'i'}, len{sizeof(char)} {} | |||||
Typestring(const std::vector<short>&) | |||||
: c_endian{host_endian_char}, c_type{'i'}, len{sizeof(short)} {} | |||||
Typestring(const std::vector<int>&) | |||||
: c_endian{host_endian_char}, c_type{'i'}, len{sizeof(int)} {} | |||||
Typestring(const std::vector<long>&) | |||||
: c_endian{host_endian_char}, c_type{'i'}, len{sizeof(long)} {} | |||||
Typestring(const std::vector<long long>&) | |||||
: c_endian{host_endian_char}, c_type{'i'}, len{sizeof(long long)} {} | |||||
Typestring(const std::vector<unsigned char>&) | |||||
: c_endian{no_endian_char}, | |||||
c_type{'u'}, | |||||
len{sizeof(unsigned char)} {} | |||||
Typestring(const std::vector<unsigned short>&) | |||||
: c_endian{host_endian_char}, | |||||
c_type{'u'}, | |||||
len{sizeof(unsigned short)} {} | |||||
Typestring(const std::vector<unsigned int>&) | |||||
: c_endian{host_endian_char}, | |||||
c_type{'u'}, | |||||
len{sizeof(unsigned int)} {} | |||||
Typestring(const std::vector<unsigned long>&) | |||||
: c_endian{host_endian_char}, | |||||
c_type{'u'}, | |||||
len{sizeof(unsigned long)} {} | |||||
Typestring(const std::vector<unsigned long long>&) | |||||
: c_endian{host_endian_char}, | |||||
c_type{'u'}, | |||||
len{sizeof(unsigned long long)} {} | |||||
Typestring(const std::vector<std::complex<float>>&) | |||||
: c_endian{host_endian_char}, | |||||
c_type{'c'}, | |||||
len{sizeof(std::complex<float>)} {} | |||||
Typestring(const std::vector<std::complex<double>>&) | |||||
: c_endian{host_endian_char}, | |||||
c_type{'c'}, | |||||
len{sizeof(std::complex<double>)} {} | |||||
Typestring(const std::vector<std::complex<long double>>&) | |||||
: c_endian{host_endian_char}, | |||||
c_type{'c'}, | |||||
len{sizeof(std::complex<long double>)} {} | |||||
}; | |||||
inline void parse_typestring(std::string typestring) { | |||||
std::regex re("'([<>|])([ifuc])(\\d+)'"); | |||||
std::smatch sm; | |||||
std::regex_match(typestring, sm, re); | |||||
if (sm.size() != 4) { | |||||
fprintf(stderr, "invalid typestring"); | |||||
} | |||||
} | |||||
namespace pyparse { | |||||
/** | |||||
Removes leading and trailing whitespaces | |||||
*/ | |||||
inline std::string trim(const std::string& str) { | |||||
const std::string whitespace = " \t"; | |||||
auto begin = str.find_first_not_of(whitespace); | |||||
if (begin == std::string::npos) | |||||
return ""; | |||||
auto end = str.find_last_not_of(whitespace); | |||||
return str.substr(begin, end - begin + 1); | |||||
} | |||||
inline std::string get_value_from_map(const std::string& mapstr) { | |||||
size_t sep_pos = mapstr.find_first_of(":"); | |||||
if (sep_pos == std::string::npos) | |||||
return ""; | |||||
std::string tmp = mapstr.substr(sep_pos + 1); | |||||
return trim(tmp); | |||||
} | |||||
/** | |||||
Parses the string representation of a Python dict | |||||
The keys need to be known and may not appear anywhere else in the data. | |||||
*/ | |||||
inline std::unordered_map<std::string, std::string> parse_dict( | |||||
std::string in, std::vector<std::string>& keys) { | |||||
std::unordered_map<std::string, std::string> map; | |||||
if (keys.size() == 0) | |||||
return map; | |||||
in = trim(in); | |||||
// unwrap dictionary | |||||
if ((in.front() == '{') && (in.back() == '}')) | |||||
in = in.substr(1, in.length() - 2); | |||||
else { | |||||
fprintf(stderr, "Not a Python dictionary."); | |||||
} | |||||
std::vector<std::pair<size_t, std::string>> positions; | |||||
for (auto const& value : keys) { | |||||
size_t pos = in.find("'" + value + "'"); | |||||
if (pos == std::string::npos) { | |||||
fprintf(stderr, "Missing %s key.", value.c_str()); | |||||
} | |||||
std::pair<size_t, std::string> position_pair{pos, value}; | |||||
positions.push_back(position_pair); | |||||
} | |||||
// sort by position in dict | |||||
std::sort(positions.begin(), positions.end()); | |||||
for (size_t i = 0; i < positions.size(); ++i) { | |||||
std::string raw_value; | |||||
size_t begin{positions[i].first}; | |||||
size_t end{std::string::npos}; | |||||
std::string key = positions[i].second; | |||||
if (i + 1 < positions.size()) | |||||
end = positions[i + 1].first; | |||||
raw_value = in.substr(begin, end - begin); | |||||
raw_value = trim(raw_value); | |||||
if (raw_value.back() == ',') | |||||
raw_value.pop_back(); | |||||
map[key] = get_value_from_map(raw_value); | |||||
} | |||||
return map; | |||||
} | |||||
/** | |||||
Parses the string representation of a Python boolean | |||||
*/ | |||||
inline bool parse_bool(const std::string& in) { | |||||
if (in == "True") | |||||
return true; | |||||
if (in == "False") | |||||
return false; | |||||
fprintf(stderr, "Invalid python boolan."); | |||||
return false; | |||||
} | |||||
/** | |||||
Parses the string representation of a Python str | |||||
*/ | |||||
inline std::string parse_str(const std::string& in) { | |||||
if ((in.front() == '\'') && (in.back() == '\'')) | |||||
return in.substr(1, in.length() - 2); | |||||
fprintf(stderr, "Invalid python string."); | |||||
return ""; | |||||
} | |||||
/** | |||||
Parses the string represenatation of a Python tuple into a vector of its items | |||||
*/ | |||||
inline std::vector<std::string> parse_tuple(std::string in) { | |||||
std::vector<std::string> v; | |||||
const char seperator = ','; | |||||
in = trim(in); | |||||
if ((in.front() == '(') && (in.back() == ')')) | |||||
in = in.substr(1, in.length() - 2); | |||||
else { | |||||
fprintf(stderr, "Invalid Python tuple."); | |||||
} | |||||
std::istringstream iss(in); | |||||
for (std::string token; std::getline(iss, token, seperator);) { | |||||
v.push_back(token); | |||||
} | |||||
return v; | |||||
} | |||||
template <typename T> | |||||
inline std::string write_tuple(const std::vector<T>& v) { | |||||
if (v.size() == 0) | |||||
return ""; | |||||
std::ostringstream ss; | |||||
if (v.size() == 1) { | |||||
ss << "(" << v.front() << ",)"; | |||||
} else { | |||||
const std::string delimiter = ", "; | |||||
// v.size() > 1 | |||||
ss << "("; | |||||
std::copy(v.begin(), v.end() - 1, | |||||
std::ostream_iterator<T>(ss, delimiter.c_str())); | |||||
ss << v.back(); | |||||
ss << ")"; | |||||
} | |||||
return ss.str(); | |||||
} | |||||
inline std::string write_boolean(bool b) { | |||||
if (b) | |||||
return "True"; | |||||
else | |||||
return "False"; | |||||
} | |||||
} // namespace pyparse | |||||
inline void parse_header(std::string header, std::string& descr) { | |||||
/* | |||||
The first 6 bytes are a magic string: exactly "x93NUMPY". | |||||
The next 1 byte is an unsigned byte: the major version number of the file | |||||
format, e.g. x01. The next 1 byte is an unsigned byte: the minor version | |||||
number of the file format, e.g. x00. Note: the version of the file format | |||||
is not tied to the version of the numpy package. The next 2 bytes form a | |||||
little-endian unsigned short int: the length of the header data | |||||
HEADER_LEN. The next HEADER_LEN bytes form the header data describing the | |||||
array's format. It is an ASCII string which contains a Python literal | |||||
expression of a dictionary. It is terminated by a newline ('n') and | |||||
padded with spaces | |||||
('x20') to make the total length of the magic string + 4 + HEADER_LEN be | |||||
evenly divisible by 16 for alignment purposes. The dictionary contains | |||||
three keys: | |||||
"descr" : dtype.descr | |||||
An object that can be passed as an argument to the numpy.dtype() | |||||
constructor to create the array's dtype. For repeatability and | |||||
readability, this dictionary is formatted using pprint.pformat() so the | |||||
keys are in alphabetic order. | |||||
*/ | |||||
// remove trailing newline | |||||
if (header.back() != '\n') | |||||
fprintf(stderr, "invalid header"); | |||||
header.pop_back(); | |||||
// parse the dictionary | |||||
std::vector<std::string> keys{"descr"}; | |||||
auto dict_map = npy::pyparse::parse_dict(header, keys); | |||||
if (dict_map.size() == 0) | |||||
fprintf(stderr, "invalid dictionary in header"); | |||||
std::string descr_s = dict_map["descr"]; | |||||
parse_typestring(descr_s); | |||||
// remove | |||||
descr = npy::pyparse::parse_str(descr_s); | |||||
return; | |||||
} | |||||
inline void parse_header(std::string header, std::string& descr, | |||||
bool& fortran_order, | |||||
std::vector<ndarray_len_t>& shape) { | |||||
/* | |||||
The first 6 bytes are a magic string: exactly "x93NUMPY". | |||||
The next 1 byte is an unsigned byte: the major version number of the file | |||||
format, e.g. x01. The next 1 byte is an unsigned byte: the minor version | |||||
number of the file format, e.g. x00. Note: the version of the file format | |||||
is not tied to the version of the numpy package. The next 2 bytes form a | |||||
little-endian unsigned short int: the length of the header data | |||||
HEADER_LEN. The next HEADER_LEN bytes form the header data describing the | |||||
array's format. It is an ASCII string which contains a Python literal | |||||
expression of a dictionary. It is terminated by a newline ('n') and | |||||
padded with spaces | |||||
('x20') to make the total length of the magic string + 4 + HEADER_LEN be | |||||
evenly divisible by 16 for alignment purposes. The dictionary contains | |||||
three keys: | |||||
"descr" : dtype.descr | |||||
An object that can be passed as an argument to the numpy.dtype() | |||||
constructor to create the array's dtype. "fortran_order" : bool Whether | |||||
the array data is Fortran-contiguous or not. Since Fortran-contiguous | |||||
arrays are a common form of non-C-contiguity, we allow them to be written | |||||
directly to disk for efficiency. "shape" : tuple of int The shape of the | |||||
array. For repeatability and readability, this dictionary is formatted | |||||
using pprint.pformat() so the keys are in alphabetic order. | |||||
*/ | |||||
// remove trailing newline | |||||
if (header.back() != '\n') | |||||
fprintf(stderr, "invalid header"); | |||||
header.pop_back(); | |||||
// parse the dictionary | |||||
std::vector<std::string> keys{"descr", "fortran_order", "shape"}; | |||||
auto dict_map = npy::pyparse::parse_dict(header, keys); | |||||
if (dict_map.size() == 0) | |||||
fprintf(stderr, "invalid dictionary in header"); | |||||
std::string descr_s = dict_map["descr"]; | |||||
std::string fortran_s = dict_map["fortran_order"]; | |||||
std::string shape_s = dict_map["shape"]; | |||||
// TODO: extract info from typestring | |||||
parse_typestring(descr_s); | |||||
// remove | |||||
descr = npy::pyparse::parse_str(descr_s); | |||||
// convert literal Python bool to C++ bool | |||||
fortran_order = npy::pyparse::parse_bool(fortran_s); | |||||
// parse the shape tuple | |||||
auto shape_v = npy::pyparse::parse_tuple(shape_s); | |||||
if (shape_v.size() == 0) | |||||
fprintf(stderr, "invalid shape tuple in header"); | |||||
for (auto item : shape_v) { | |||||
ndarray_len_t dim = static_cast<ndarray_len_t>(std::stoul(item)); | |||||
shape.push_back(dim); | |||||
} | |||||
} | |||||
inline std::string write_header_dict(const std::string& descr, | |||||
bool fortran_order, | |||||
const std::vector<ndarray_len_t>& shape) { | |||||
std::string s_fortran_order = npy::pyparse::write_boolean(fortran_order); | |||||
std::string shape_s = npy::pyparse::write_tuple(shape); | |||||
return "{'descr': '" + descr + "', 'fortran_order': " + s_fortran_order + | |||||
", 'shape': " + shape_s + ", }"; | |||||
} | |||||
inline void write_header(std::ostream& out, const std::string& descr, | |||||
bool fortran_order, | |||||
const std::vector<ndarray_len_t>& shape_v) { | |||||
std::string header_dict = write_header_dict(descr, fortran_order, shape_v); | |||||
size_t length = magic_string_length + 2 + 2 + header_dict.length() + 1; | |||||
unsigned char version[2] = {1, 0}; | |||||
if (length >= 255 * 255) { | |||||
length = magic_string_length + 2 + 4 + header_dict.length() + 1; | |||||
version[0] = 2; | |||||
version[1] = 0; | |||||
} | |||||
size_t padding_len = 16 - length % 16; | |||||
std::string padding(padding_len, ' '); | |||||
// write magic | |||||
write_magic(out, version[0], version[1]); | |||||
// write header length | |||||
if (version[0] == 1 && version[1] == 0) { | |||||
char header_len_le16[2]; | |||||
uint16_t header_len = static_cast<uint16_t>(header_dict.length() + | |||||
padding.length() + 1); | |||||
header_len_le16[0] = (header_len >> 0) & 0xff; | |||||
header_len_le16[1] = (header_len >> 8) & 0xff; | |||||
out.write(reinterpret_cast<char*>(header_len_le16), 2); | |||||
} else { | |||||
char header_len_le32[4]; | |||||
uint32_t header_len = static_cast<uint32_t>(header_dict.length() + | |||||
padding.length() + 1); | |||||
header_len_le32[0] = (header_len >> 0) & 0xff; | |||||
header_len_le32[1] = (header_len >> 8) & 0xff; | |||||
header_len_le32[2] = (header_len >> 16) & 0xff; | |||||
header_len_le32[3] = (header_len >> 24) & 0xff; | |||||
out.write(reinterpret_cast<char*>(header_len_le32), 4); | |||||
} | |||||
out << header_dict << padding << '\n'; | |||||
} | |||||
inline std::string read_header(std::istream& istream) { | |||||
// check magic bytes an version number | |||||
unsigned char v_major, v_minor; | |||||
read_magic(istream, v_major, v_minor); | |||||
uint32_t header_length = 0; | |||||
if (v_major == 1 && v_minor == 0) { | |||||
char header_len_le16[2]; | |||||
istream.read(header_len_le16, 2); | |||||
header_length = (header_len_le16[0] << 0) | (header_len_le16[1] << 8); | |||||
if ((magic_string_length + 2 + 2 + header_length) % 16 != 0) { | |||||
// TODO: display warning | |||||
} | |||||
} else if (v_major == 2 && v_minor == 0) { | |||||
char header_len_le32[4]; | |||||
istream.read(header_len_le32, 4); | |||||
header_length = (header_len_le32[0] << 0) | (header_len_le32[1] << 8) | | |||||
(header_len_le32[2] << 16) | (header_len_le32[3] << 24); | |||||
if ((magic_string_length + 2 + 4 + header_length) % 16 != 0) { | |||||
// TODO: display warning | |||||
} | |||||
} else { | |||||
fprintf(stderr, "unsupported file format version"); | |||||
} | |||||
auto buf_v = std::vector<char>(); | |||||
buf_v.reserve(header_length); | |||||
istream.read(buf_v.data(), header_length); | |||||
std::string header(buf_v.data(), header_length); | |||||
return header; | |||||
} | |||||
inline ndarray_len_t comp_size(const std::vector<ndarray_len_t>& shape) { | |||||
ndarray_len_t size = 1; | |||||
for (ndarray_len_t i : shape) | |||||
size *= i; | |||||
return size; | |||||
} | |||||
template <typename Scalar> | |||||
inline void SaveArrayAsNumpy(const std::string& filename, bool fortran_order, | |||||
unsigned int n_dims, const unsigned long shape[], | |||||
const std::vector<Scalar>& data) { | |||||
Typestring typestring_o(data); | |||||
std::string typestring = typestring_o.str(); | |||||
std::ofstream stream(filename, std::ofstream::binary); | |||||
if (!stream) { | |||||
fprintf(stderr, "io error: failed to open a file."); | |||||
} | |||||
std::vector<ndarray_len_t> shape_v(shape, shape + n_dims); | |||||
write_header(stream, typestring, fortran_order, shape_v); | |||||
auto size = static_cast<size_t>(comp_size(shape_v)); | |||||
stream.write(reinterpret_cast<const char*>(data.data()), | |||||
sizeof(Scalar) * size); | |||||
} | |||||
template <typename Scalar> | |||||
inline void LoadArrayFromNumpy(const std::string& filename, | |||||
std::vector<unsigned long>& shape, | |||||
std::vector<Scalar>& data) { | |||||
bool fortran_order; | |||||
LoadArrayFromNumpy<Scalar>(filename, shape, fortran_order, data); | |||||
} | |||||
template <typename Scalar> | |||||
inline void LoadArrayFromNumpy(const std::string& filename, | |||||
std::vector<unsigned long>& shape, | |||||
bool& fortran_order, std::vector<Scalar>& data) { | |||||
std::ifstream stream(filename, std::ifstream::binary); | |||||
if (!stream) { | |||||
fprintf(stderr, "io error: failed to open a file."); | |||||
} | |||||
std::string header = read_header(stream); | |||||
// parse header | |||||
std::string typestr; | |||||
parse_header(header, typestr, fortran_order, shape); | |||||
// check if the typestring matches the given one | |||||
Typestring typestring_o{data}; | |||||
std::string expect_typestr = typestring_o.str(); | |||||
if (typestr != expect_typestr) { | |||||
fprintf(stderr, "formatting error: typestrings not matching"); | |||||
} | |||||
// compute the data size based on the shape | |||||
auto size = static_cast<size_t>(comp_size(shape)); | |||||
data.resize(size); | |||||
// read the data | |||||
stream.read(reinterpret_cast<char*>(data.data()), sizeof(Scalar) * size); | |||||
} | |||||
inline void LoadArrayFromNumpy(const std::string& filename, | |||||
std::string& type_str, | |||||
std::vector<ndarray_len_t>& shape, | |||||
std::vector<int8_t>& data) { | |||||
std::ifstream stream(filename, std::ifstream::binary); | |||||
if (!stream) { | |||||
fprintf(stderr, "io error: failed to open a file."); | |||||
} | |||||
std::string header = read_header(stream); | |||||
bool fortran_order; | |||||
// parse header | |||||
parse_header(header, type_str, fortran_order, shape); | |||||
// check if the typestring matches the given one | |||||
std::string size_str = type_str.substr(type_str.size() - 1); | |||||
size_t elem_size = atoi(size_str.c_str()); | |||||
// compute the data size based on the shape | |||||
auto byte_size = elem_size * static_cast<size_t>(comp_size(shape)); | |||||
data.resize(byte_size); | |||||
// read the data | |||||
stream.read(reinterpret_cast<char*>(data.data()), byte_size); | |||||
} | |||||
} // namespace npy | |||||
#endif // NPY_H |
@@ -1,108 +0,0 @@ | |||||
/** | |||||
* \file sdk/load-and-run/src/text_table.cpp | |||||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||||
* | |||||
* Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
* implied. | |||||
*/ | |||||
#include "text_table.h" | |||||
using namespace mgb; | |||||
namespace { | |||||
inline void mid(std::ostream& os, const std::string& str, size_t max_w) { | |||||
size_t l = (max_w - str.length()) / 2 + str.length(); | |||||
size_t r = max_w - l; | |||||
os << std::setw(l) << std::right << str; | |||||
if (r > 0) os << std::setw(r) << ' '; | |||||
} | |||||
inline size_t char_length(char c) { return c ? 1 : 0; } | |||||
} // namespace | |||||
void TextTable::adjuster_last_row() { | |||||
if (m_rows.empty()) return; | |||||
auto& row = m_rows.back(); | |||||
if (row.params.horizontal == 0 or row.params.vertical == 0) { | |||||
row.params.corner = 0; | |||||
} | |||||
if (row.params.horizontal != 0 && row.params.vertical != 0 && | |||||
row.params.corner == 0) { | |||||
row.params.corner = row.params.horizontal; | |||||
} | |||||
} | |||||
void TextTable::show(std::ostream& os) { | |||||
if (m_rows.empty()) return; | |||||
auto& last_row = m_rows.front(); | |||||
bool first = true; | |||||
for (auto& row : m_rows) { | |||||
auto& lrow = | |||||
(last_row.values.size() * char_length(last_row.params.horizontal)) > | |||||
(row.values.size() * char_length(row.params.horizontal)) | |||||
? last_row | |||||
: row; | |||||
// line before row | |||||
if (lrow.params.horizontal) { | |||||
if (not first) os << std::endl; | |||||
os << m_prefix; | |||||
if (lrow.params.corner) os << lrow.params.corner; | |||||
size_t skip_size = 0; | |||||
// table name | |||||
if (first) { | |||||
os << m_name; | |||||
skip_size = m_name.length(); | |||||
} | |||||
for (size_t i = 0; i < lrow.values.size(); ++i) { | |||||
auto max_w = m_cols_max_w.at(i) + m_padding * 2; | |||||
if (max_w + char_length(lrow.params.corner) <= skip_size) { | |||||
skip_size = | |||||
skip_size - max_w - char_length(lrow.params.corner); | |||||
continue; | |||||
} | |||||
size_t rest = | |||||
max_w + char_length(lrow.params.corner) - skip_size; | |||||
skip_size = 0; | |||||
if (rest > char_length(lrow.params.corner)) { | |||||
os << std::string(rest - char_length(lrow.params.corner), | |||||
lrow.params.horizontal); | |||||
rest = char_length(lrow.params.corner); | |||||
} | |||||
if (rest > 0 && lrow.params.corner) os << lrow.params.corner; | |||||
} | |||||
} else if (first) { | |||||
os << m_prefix << ' ' << m_name; | |||||
} | |||||
first = false; | |||||
os << std::endl << m_prefix; | |||||
if (row.params.vertical) os << row.params.vertical; | |||||
// row | |||||
for (size_t i = 0; i < row.values.size(); ++i) { | |||||
auto& str = row.values.at(i); | |||||
auto max_w = m_cols_max_w.at(i) + 2 * m_padding; | |||||
if (row.params.align == Align::Mid) { | |||||
mid(os, str, max_w); | |||||
} else if (row.params.align == Align::Left) { | |||||
os << std::setw(max_w) << std::left << str; | |||||
} else { | |||||
os << std::setw(max_w) << std::right << str; | |||||
} | |||||
if (row.params.vertical) os << row.params.vertical; | |||||
} | |||||
last_row = row; | |||||
} | |||||
if (last_row.params.horizontal) { | |||||
os << std::endl << m_prefix; | |||||
if (last_row.params.corner) os << last_row.params.corner; | |||||
for (size_t i = 0; i < last_row.values.size(); ++i) { | |||||
auto max_w = m_cols_max_w.at(i); | |||||
std::string tmp(max_w + m_padding * 2, last_row.params.horizontal); | |||||
os << tmp; | |||||
if (last_row.params.corner) os << last_row.params.corner; | |||||
} | |||||
} | |||||
} |
@@ -1,132 +0,0 @@ | |||||
/** | |||||
* \file sdk/load-and-run/src/text_table.h | |||||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||||
* | |||||
* Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
* implied. | |||||
*/ | |||||
#pragma once | |||||
#include <array> | |||||
#include <iomanip> | |||||
#include <ostream> | |||||
#include <sstream> | |||||
#include <string> | |||||
#include <tuple> | |||||
#include <type_traits> | |||||
#include <vector> | |||||
#include "megbrain/common.h" | |||||
namespace mgb | |||||
{ | |||||
class TextTable { | |||||
public: | |||||
enum Level { Summary, Detail }; | |||||
enum class Align : int { Left, Right, Mid }; | |||||
explicit TextTable(const std::string& table_name) : m_name(table_name) {} | |||||
TextTable& horizontal(char c) { | |||||
m_row.params.horizontal = c; | |||||
return *this; | |||||
} | |||||
TextTable& vertical(char c) { | |||||
m_row.params.vertical = c; | |||||
return *this; | |||||
} | |||||
TextTable& corner(char c) { | |||||
m_row.params.corner = c; | |||||
return *this; | |||||
} | |||||
TextTable& align(Align v) { | |||||
m_row.params.align = v; | |||||
return *this; | |||||
} | |||||
TextTable& padding(size_t w) { | |||||
m_padding = w; | |||||
return *this; | |||||
} | |||||
TextTable& prefix(const std::string& str) { | |||||
m_prefix = str; | |||||
return *this; | |||||
} | |||||
template <typename T> | |||||
TextTable& add(const T& value) { | |||||
m_row.values.emplace_back(value); | |||||
if (m_cols_max_w.size() < m_row.values.size()) { | |||||
m_cols_max_w.emplace_back(m_row.values.back().length()); | |||||
} else { | |||||
mgb_assert(m_row.values.size() >= 1); | |||||
size_t i = m_row.values.size() - 1; | |||||
m_cols_max_w[i] = | |||||
std::max(m_cols_max_w[i], m_row.values.back().length()); | |||||
} | |||||
return *this; | |||||
} | |||||
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = 0> | |||||
TextTable& add(const T& value) { | |||||
std::stringstream ss; | |||||
ss << std::setiosflags(std::ios::fixed) << std::setprecision(2); | |||||
ss << value; | |||||
m_row.values.emplace_back(ss.str()); | |||||
if (m_cols_max_w.size() < m_row.values.size()) { | |||||
m_cols_max_w.emplace_back(m_row.values.back().length()); | |||||
} else { | |||||
mgb_assert(m_row.values.size() >= 1); | |||||
size_t i = m_row.values.size() - 1; | |||||
m_cols_max_w[i] = | |||||
std::max(m_cols_max_w[i], m_row.values.back().length()); | |||||
} | |||||
return *this; | |||||
} | |||||
template <typename T, typename std::enable_if<std::is_integral<T>::value, bool>::type = 0> | |||||
TextTable& add(const T& value) { | |||||
m_row.values.emplace_back(std::to_string(value)); | |||||
return *this; | |||||
} | |||||
void eor() { | |||||
m_rows.emplace_back(m_row); | |||||
adjuster_last_row(); | |||||
m_row.values.clear(); | |||||
} | |||||
void reset() { | |||||
m_row = {}; | |||||
m_cols_max_w.clear(); | |||||
m_padding = 0; | |||||
m_rows.clear(); | |||||
} | |||||
void show(std::ostream& os); | |||||
private: | |||||
void adjuster_last_row(); | |||||
std::string m_name; | |||||
std::vector<size_t> m_cols_max_w; | |||||
size_t m_padding = 0; | |||||
std::string m_prefix = ""; | |||||
struct Row { | |||||
std::vector<std::string> values; | |||||
struct Params { | |||||
Align align = Align::Left; | |||||
char horizontal = '-', vertical = '|', corner = '+'; | |||||
} params; | |||||
}; | |||||
std::vector<Row> m_rows; | |||||
Row m_row; | |||||
}; | |||||
inline std::ostream& operator<<(std::ostream& stream, TextTable& table) { | |||||
table.show(stream); | |||||
return stream; | |||||
} | |||||
} // namespace mgb |
@@ -1,74 +0,0 @@ | |||||
/** | |||||
* \file sdk/load-and-run/test/test_json_loader.cpp | |||||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||||
* | |||||
* Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
*/ | |||||
#include <cfloat> | |||||
#include <cstdint> | |||||
#include <cstdio> | |||||
#include <cmath> | |||||
#include "../src/json_loader.h" | |||||
using namespace mgb; | |||||
void test_number(double real, std::string str) { | |||||
JsonLoader json; | |||||
auto root = json.load(str.data(), str.size()); | |||||
mgb_assert(root->is_number()); | |||||
mgb_assert(std::fabs(real - root->number()) <= DBL_EPSILON); | |||||
} | |||||
void test_string(std::string str, std::string json_str) { | |||||
JsonLoader json; | |||||
auto root = json.load(json_str.data(), json_str.size()); | |||||
mgb_assert(root->is_str()); | |||||
mgb_assert(str == root->str()); | |||||
} | |||||
void test_array(size_t num, std::string str) { | |||||
JsonLoader json; | |||||
auto root = json.load(str.data(), str.size()); | |||||
mgb_assert(root->is_array()); | |||||
mgb_assert(root->len() == num); | |||||
} | |||||
void test_object(size_t num, std::string str) { | |||||
JsonLoader json; | |||||
auto root = json.load(str.data(), str.size()); | |||||
mgb_assert(root->is_object()); | |||||
mgb_assert(root->len() == num); | |||||
} | |||||
int main() { | |||||
test_number(1.0, "1.0"); | |||||
test_number(1e10, "1e10"); | |||||
test_number(0.2345678, "0.02345678e1"); | |||||
test_number(-10086, "-1.0086E4"); | |||||
test_number(1.7976931348623157e+308, | |||||
"1.7976931348623157e+308"); // max double | |||||
test_string("a", "\"a\""); | |||||
test_string("\\table", "\"\\table\""); | |||||
test_array(0, " [ ] "); | |||||
test_array(4, " [ 0.1, 0.2,0.3, 1990 ] "); | |||||
test_array(2, " [ 0.1, \"hello-world\"]"); | |||||
test_array(3, " [ 0.1, \"hello-world\", [2.0, 33]]"); | |||||
test_array(1, " [ [ [ [2020] ], [2021], [[2022]] ] ]"); | |||||
test_object(0, " { } "); | |||||
test_object(1, "{\"key1\": 2023}"); | |||||
test_object(1, | |||||
"{\"key1\": { \"key2\": { " | |||||
"\"key3\": \"value\" } } }"); | |||||
test_object(1, "{\"key1\":{\"key2\":{}}}"); | |||||
printf("test passed\n"); | |||||
return 0; | |||||
} |