diff --git a/src/bb/image-io/bb.h b/src/bb/image-io/bb.h index 0d3bc7f8..0f2185cd 100644 --- a/src/bb/image-io/bb.h +++ b/src/bb/image-io/bb.h @@ -10,1263 +10,1256 @@ namespace ion { namespace bb { -namespace image_io { - -class BayerMap { -public: - enum class Pattern { - RGGB, - BGGR, - GRBG, - GBRG - }; - - static const std::map enum_map; - - // R: 0 - // G: 1 - // B: 2 - static Halide::Expr get_color(Pattern pat, Halide::Expr x, Halide::Expr y) { - return Halide::select( - y % 2 == 0, - Halide::select( - x % 2 == 0, - get_color(pat, 0), - get_color(pat, 1)), - Halide::select( - x % 2 == 0, - get_color(pat, 2), - get_color(pat, 3))); - } - -private: - static const int bayer_map[4][4]; - - static int get_color(Pattern pat, int pos) { - return bayer_map[static_cast(pat)][pos]; - } -}; - -const std::map BayerMap::enum_map{ - {"RGGB", BayerMap::Pattern::RGGB}, - {"BGGR", BayerMap::Pattern::BGGR}, - {"GRBG", BayerMap::Pattern::GRBG}, - {"GBRG", BayerMap::Pattern::GBRG}}; - -const int BayerMap::bayer_map[4][4]{ - {0, 1, 1, 2}, // RGGB - {2, 1, 1, 0}, // BGGR - {1, 0, 2, 1}, // GRBG - {1, 2, 0, 1} // GBRG -}; + namespace image_io { + + class BayerMap { + public: + enum class Pattern { + RGGB, + BGGR, + GRBG, + GBRG + }; -#ifdef __linux__ -uint32_t make_pixel_format(BayerMap::Pattern bayer_pattern, int32_t bit_width) -{ - uint32_t pix_format; - switch (bit_width * 10 + static_cast(static_cast(bayer_pattern))) { - case 80: // RGGB 8bit - pix_format = V4L2_PIX_FMT_SRGGB8; - break; - case 81: // BGGR 8bit - pix_format = V4L2_PIX_FMT_SBGGR8; - break; - case 82: // GRBG 8bit - pix_format = V4L2_PIX_FMT_SGRBG8; - break; - case 83: // GBRG 8bit - pix_format = V4L2_PIX_FMT_SGBRG8; - break; - case 100: // RGGB 10bit - pix_format = V4L2_PIX_FMT_SRGGB10; - break; - case 101: // BGGR 10bit - pix_format = V4L2_PIX_FMT_SBGGR10; - break; - case 102: // GRBG 10bit - pix_format = V4L2_PIX_FMT_SGRBG10; - break; - case 103: // GBRG 10bit - pix_format = V4L2_PIX_FMT_SGBRG10; - break; - case 120: // RGGB 12bit - pix_format = V4L2_PIX_FMT_SRGGB12; - break; - case 121: // BGGR 12bit - pix_format = V4L2_PIX_FMT_SBGGR12; - break; - case 122: // GRBG 12bit - pix_format = V4L2_PIX_FMT_SGRBG12; - break; - case 123: // GBRG 12bit - pix_format = V4L2_PIX_FMT_SGBRG12; - break; - default: - throw std::runtime_error("Unsupported pixel_format combination"); - } - - return pix_format; -} - -int instance_id = 0; - - -class Camera : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "USBCamera"}; - BuildingBlockParam gc_description{"gc_description", "This captures USB camera image."}; - BuildingBlockParam gc_tags{"gc_tags", "input,sensor"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height), 3] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam fps{"fps", 30}; - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - BuildingBlockParam index{"index", 0}; - BuildingBlockParam url{"url", ""}; - - Output output{"output", Halide::type_of(), 3}; - - void generate() { - using namespace Halide; - std::string url_str = url; - Halide::Buffer url_buf(url_str.size() + 1); - url_buf.fill(0); - std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); - - std::vector params = {instance_id++, cast(index), cast(fps), cast(width), cast(height), url_buf}; - Func camera(static_cast(gc_prefix) + "camera"); - camera.define_extern("ion_bb_image_io_camera", params, Halide::type_of(), 2); - camera.compute_root(); - - Func camera_ = BoundaryConditions::repeat_edge(camera, {{0, 2 * width}, {0, height}}); - - Var c, x, y; - - Expr yv = cast(camera_(2 * x, y)); - Expr uv = cast(camera_(select((x & 1) == 0, 2 * x + 1, 2 * x - 1), y)); - Expr vv = cast(camera_(select((x & 1) == 0, 2 * x + 3, 2 * x + 1), y)); - - Expr f128 = cast(128); - - Expr r = saturating_cast(yv + cast(1.403f) * (vv - f128)); - Expr g = saturating_cast(yv - cast(0.344f) * (uv - f128) - (cast(0.714f) * (vv - f128))); - Expr b = saturating_cast(yv + cast(1.773f) * (uv - f128)); - - Func f(static_cast(gc_prefix) + "output"); - f(x, y, c) = mux(c, {r, g, b}); - - output = f; - } -}; - -class Camera2 : public ion::BuildingBlock { -public: - BuildingBlockParam num_devices{"num_devices", 2}; - BuildingBlockParam gc_title{"gc_title", "USBCamera"}; - BuildingBlockParam gc_description{"gc_description", "This captures USB camera image."}; - BuildingBlockParam gc_tags{"gc_tags", "input,sensor"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height), 3] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam fps{"fps", 30}; - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - BuildingBlockParam index{"index", 0}; - BuildingBlockParam url0{"url0", ""}; - BuildingBlockParam url1{"url1", ""}; - - - - Output output0{"output0", Halide::type_of(), 3}; - Output output1{"output1", Halide::type_of(), 3}; - - - void generate() { - using namespace Halide; - - - for (int i =0; i < num_devices; i++){ - std::string url_str; - if(i == 0){ - url_str = url0; - } - else{ - url_str = url1; + static const std::map enum_map; + + // R: 0 + // G: 1 + // B: 2 + static Halide::Expr get_color(Pattern pat, Halide::Expr x, Halide::Expr y) + { + return Halide::select( + y % 2 == 0, + Halide::select( + x % 2 == 0, + get_color(pat, 0), + get_color(pat, 1)), + Halide::select( + x % 2 == 0, + get_color(pat, 2), + get_color(pat, 3))); } - Halide::Buffer url_buf(url_str.size() + 1); - url_buf.fill(0); - std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); - - std::vector params = {instance_id++, cast(index), cast(fps), cast(width), cast(height), url_buf}; - Func camera(static_cast(gc_prefix) + "camera"); - camera.define_extern("ion_bb_image_io_camera", params, Halide::type_of(), 2); - camera.compute_root(); - - Func camera_ = BoundaryConditions::repeat_edge(camera, {{0, 2 * width}, {0, height}}); - - Var c, x, y; - - Expr yv = cast(camera_(2 * x, y)); - Expr uv = cast(camera_(select((x & 1) == 0, 2 * x + 1, 2 * x - 1), y)); - Expr vv = cast(camera_(select((x & 1) == 0, 2 * x + 3, 2 * x + 1), y)); - - Expr f128 = cast(128); - Expr r = saturating_cast(yv + cast(1.403f) * (vv - f128)); - Expr g = saturating_cast(yv - cast(0.344f) * (uv - f128) - (cast(0.714f) * (vv - f128))); - Expr b = saturating_cast(yv + cast(1.773f) * (uv - f128)); - + private: + static const int bayer_map[4][4]; + static int get_color(Pattern pat, int pos) + { + return bayer_map[static_cast(pat)][pos]; + } + }; + const std::map BayerMap::enum_map { + { "RGGB", BayerMap::Pattern::RGGB }, + { "BGGR", BayerMap::Pattern::BGGR }, + { "GRBG", BayerMap::Pattern::GRBG }, + { "GBRG", BayerMap::Pattern::GBRG } + }; - Func f(static_cast(gc_prefix) + "output" + std::to_string(i)); - f(x, y, c) = mux(c, {r, g, b}); + const int BayerMap::bayer_map[4][4] { + { 0, 1, 1, 2 }, // RGGB + { 2, 1, 1, 0 }, // BGGR + { 1, 0, 2, 1 }, // GRBG + { 1, 2, 0, 1 } // GBRG + }; +#ifdef __linux__ + uint32_t make_pixel_format(BayerMap::Pattern bayer_pattern, int32_t bit_width) + { + uint32_t pix_format; + switch (bit_width * 10 + static_cast(static_cast(bayer_pattern))) { + case 80: // RGGB 8bit + pix_format = V4L2_PIX_FMT_SRGGB8; + break; + case 81: // BGGR 8bit + pix_format = V4L2_PIX_FMT_SBGGR8; + break; + case 82: // GRBG 8bit + pix_format = V4L2_PIX_FMT_SGRBG8; + break; + case 83: // GBRG 8bit + pix_format = V4L2_PIX_FMT_SGBRG8; + break; + case 100: // RGGB 10bit + pix_format = V4L2_PIX_FMT_SRGGB10; + break; + case 101: // BGGR 10bit + pix_format = V4L2_PIX_FMT_SBGGR10; + break; + case 102: // GRBG 10bit + pix_format = V4L2_PIX_FMT_SGRBG10; + break; + case 103: // GBRG 10bit + pix_format = V4L2_PIX_FMT_SGBRG10; + break; + case 120: // RGGB 12bit + pix_format = V4L2_PIX_FMT_SRGGB12; + break; + case 121: // BGGR 12bit + pix_format = V4L2_PIX_FMT_SBGGR12; + break; + case 122: // GRBG 12bit + pix_format = V4L2_PIX_FMT_SGRBG12; + break; + case 123: // GBRG 12bit + pix_format = V4L2_PIX_FMT_SGBRG12; + break; + default: + throw std::runtime_error("Unsupported pixel_format combination"); + } - if (i ==0) - output0 = f; - else - output1 = f; + return pix_format; } - } -}; + int instance_id = 0; + class Camera : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "USBCamera" }; + BuildingBlockParam gc_description { "gc_description", "This captures USB camera image." }; + BuildingBlockParam gc_tags { "gc_tags", "input,sensor" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height), 3] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; -class CameraN : public ion::BuildingBlock { -public: - BuildingBlockParam num_devices{"num_devices", 2}; - BuildingBlockParam gc_title{"gc_title", "USBCamera"}; - BuildingBlockParam gc_description{"gc_description", "This captures USB camera image."}; - BuildingBlockParam gc_tags{"gc_tags", "input,sensor"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height), 3] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; + BuildingBlockParam fps { "fps", 30 }; + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + BuildingBlockParam index { "index", 0 }; + BuildingBlockParam url { "url", "" }; - BuildingBlockParam fps{"fps", 30}; - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - BuildingBlockParam index{"index", 0}; - BuildingBlockParam urls{"urls", ""}; + Output output { "output", Halide::type_of(), 3 }; - Output output{"output", Halide::type_of(), 3}; + void generate() + { + using namespace Halide; + std::string url_str = url; + Halide::Buffer url_buf(url_str.size() + 1); + url_buf.fill(0); + std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); + std::vector params = { instance_id++, cast(index), cast(fps), cast(width), cast(height), url_buf }; + Func camera(static_cast(gc_prefix) + "camera"); + camera.define_extern("ion_bb_image_io_camera", params, Halide::type_of(), 2); + camera.compute_root(); - void generate() { + Func camera_ = BoundaryConditions::repeat_edge(camera, { { 0, 2 * width }, { 0, height } }); - std::stringstream urls_stream(urls); - std::string url; - std::vector url_list; - while(std::getline(urls_stream, url, ';')) - { - url_list.push_back(url); - } + Var c, x, y; + + Expr yv = cast(camera_(2 * x, y)); + Expr uv = cast(camera_(select((x & 1) == 0, 2 * x + 1, 2 * x - 1), y)); + Expr vv = cast(camera_(select((x & 1) == 0, 2 * x + 3, 2 * x + 1), y)); + Expr f128 = cast(128); - using namespace Halide; + Expr r = saturating_cast(yv + cast(1.403f) * (vv - f128)); + Expr g = saturating_cast(yv - cast(0.344f) * (uv - f128) - (cast(0.714f) * (vv - f128))); + Expr b = saturating_cast(yv + cast(1.773f) * (uv - f128)); - output.resize(num_devices); + Func f(static_cast(gc_prefix) + "output"); + f(x, y, c) = mux(c, { r, g, b }); - for (int i =0; i < num_devices; i++){ - std::string url_str; - if (url_list.size()!=0){ - url_str = url_list[i]; + output = f; } - else{ - url_str = ""; + }; + + class Camera2 : public ion::BuildingBlock { + public: + BuildingBlockParam num_devices { "num_devices", 2 }; + BuildingBlockParam gc_title { "gc_title", "USBCamera" }; + BuildingBlockParam gc_description { "gc_description", "This captures USB camera image." }; + BuildingBlockParam gc_tags { "gc_tags", "input,sensor" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height), 3] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam fps { "fps", 30 }; + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + BuildingBlockParam index { "index", 0 }; + BuildingBlockParam url0 { "url0", "" }; + BuildingBlockParam url1 { "url1", "" }; + + Output output0 { "output0", Halide::type_of(), 3 }; + Output output1 { "output1", Halide::type_of(), 3 }; + + void generate() + { + using namespace Halide; + + for (int i = 0; i < num_devices; i++) { + std::string url_str; + if (i == 0) { + url_str = url0; + } else { + url_str = url1; + } + + Halide::Buffer url_buf(url_str.size() + 1); + url_buf.fill(0); + std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); + + std::vector params = { instance_id++, cast(index), cast(fps), cast(width), cast(height), url_buf }; + Func camera(static_cast(gc_prefix) + "camera"); + camera.define_extern("ion_bb_image_io_camera", params, Halide::type_of(), 2); + camera.compute_root(); + + Func camera_ = BoundaryConditions::repeat_edge(camera, { { 0, 2 * width }, { 0, height } }); + + Var c, x, y; + + Expr yv = cast(camera_(2 * x, y)); + Expr uv = cast(camera_(select((x & 1) == 0, 2 * x + 1, 2 * x - 1), y)); + Expr vv = cast(camera_(select((x & 1) == 0, 2 * x + 3, 2 * x + 1), y)); + + Expr f128 = cast(128); + Expr r = saturating_cast(yv + cast(1.403f) * (vv - f128)); + Expr g = saturating_cast(yv - cast(0.344f) * (uv - f128) - (cast(0.714f) * (vv - f128))); + Expr b = saturating_cast(yv + cast(1.773f) * (uv - f128)); + + Func f(static_cast(gc_prefix) + "output" + std::to_string(i)); + f(x, y, c) = mux(c, { r, g, b }); + + if (i == 0) + output0 = f; + else + output1 = f; + } } + }; + class CameraN : public ion::BuildingBlock { + public: + BuildingBlockParam num_devices { "num_devices", 2 }; + BuildingBlockParam gc_title { "gc_title", "USBCamera" }; + BuildingBlockParam gc_description { "gc_description", "This captures USB camera image." }; + BuildingBlockParam gc_tags { "gc_tags", "input,sensor" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height), 3] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam fps { "fps", 30 }; + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + BuildingBlockParam index { "index", 0 }; + BuildingBlockParam urls { "urls", "" }; + + Output output { "output", Halide::type_of(), 3 }; + + void generate() + { + + std::stringstream urls_stream(urls); + std::string url; + std::vector url_list; + while (std::getline(urls_stream, url, ';')) { + url_list.push_back(url); + } + using namespace Halide; - Halide::Buffer url_buf(url_str.size() + 1); - url_buf.fill(0); - std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); + output.resize(num_devices); - std::vector params = {instance_id++, cast(index), cast(fps), cast(width), cast(height), url_buf}; - Func camera(static_cast(gc_prefix) + "camera"); - camera.define_extern("ion_bb_image_io_camera", params, Halide::type_of(), 2); - camera.compute_root(); + for (int i = 0; i < num_devices; i++) { + std::string url_str; + if (url_list.size() != 0) { + url_str = url_list[i]; + } else { + url_str = ""; + } - Func camera_ = BoundaryConditions::repeat_edge(camera, {{0, 2 * width}, {0, height}}); + Halide::Buffer url_buf(url_str.size() + 1); + url_buf.fill(0); + std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); - Var c, x, y; + std::vector params = { instance_id++, cast(index), cast(fps), cast(width), cast(height), url_buf }; + Func camera(static_cast(gc_prefix) + "camera"); + camera.define_extern("ion_bb_image_io_camera", params, Halide::type_of(), 2); + camera.compute_root(); - Expr yv = cast(camera_(2 * x, y)); - Expr uv = cast(camera_(select((x & 1) == 0, 2 * x + 1, 2 * x - 1), y)); - Expr vv = cast(camera_(select((x & 1) == 0, 2 * x + 3, 2 * x + 1), y)); + Func camera_ = BoundaryConditions::repeat_edge(camera, { { 0, 2 * width }, { 0, height } }); - Expr f128 = cast(128); - Expr r = saturating_cast(yv + cast(1.403f) * (vv - f128)); - Expr g = saturating_cast(yv - cast(0.344f) * (uv - f128) - (cast(0.714f) * (vv - f128))); - Expr b = saturating_cast(yv + cast(1.773f) * (uv - f128)); + Var c, x, y; + Expr yv = cast(camera_(2 * x, y)); + Expr uv = cast(camera_(select((x & 1) == 0, 2 * x + 1, 2 * x - 1), y)); + Expr vv = cast(camera_(select((x & 1) == 0, 2 * x + 3, 2 * x + 1), y)); - Func f(static_cast(gc_prefix) + "output" + std::to_string(i)); - f(x, y, c) = mux(c, {r, g, b}); + Expr f128 = cast(128); + Expr r = saturating_cast(yv + cast(1.403f) * (vv - f128)); + Expr g = saturating_cast(yv - cast(0.344f) * (uv - f128) - (cast(0.714f) * (vv - f128))); + Expr b = saturating_cast(yv + cast(1.773f) * (uv - f128)); - output[i](_) = f(_); - } + Func f(static_cast(gc_prefix) + "output" + std::to_string(i)); + f(x, y, c) = mux(c, { r, g, b }); - } -}; - -class IMX219 : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "IMX219"}; - BuildingBlockParam gc_description{"gc_description", "This captures IMX219 image."}; - BuildingBlockParam gc_tags{"gc_tags", "input,sensor"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height)] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam fps{"fps", 24}; - BuildingBlockParam width{"width", 3264}; - BuildingBlockParam height{"height", 2464}; - BuildingBlockParam index{"index", 0}; - BuildingBlockParam url{"url", ""}; - BuildingBlockParam force_sim_mode{"force_sim_mode", false}; - - Output output{"output", Halide::type_of(), 2}; - - void generate() { - using namespace Halide; - std::string url_str = url; - Halide::Buffer url_buf(url_str.size() + 1); - url_buf.fill(0); - std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); - - std::vector params = { - instance_id++, - cast(index), - cast(fps), - cast(width), - cast(height), - cast(Expr(V4L2_PIX_FMT_SRGGB10)), - cast(force_sim_mode), - url_buf, - 0.4f, 0.5f, 0.3125f, - 0.0625f, - 10, 6 + output[i](_) = f(_); + } + } }; - Func v4l2_imx219(static_cast(gc_prefix) + "output"); - v4l2_imx219.define_extern("ion_bb_image_io_v4l2", params, type_of(), 2); - v4l2_imx219.compute_root(); - - output = v4l2_imx219; - } -}; - -class D435 : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "D435"}; - BuildingBlockParam gc_description{"gc_description", "This captures D435 stereo image and depth."}; - BuildingBlockParam gc_tags{"gc_tags", "input,sensor"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output_l: [1280, 720], output_r: [1280, 720], output_d: [1280, 720] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", ""}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - Output output_l{"output_l", Halide::type_of(), 2}; - Output output_r{"output_r", Halide::type_of(), 2}; - Output output_d{"output_d", Halide::type_of(), 2}; - - void generate() { - using namespace Halide; - Func realsense_d435_frameset(static_cast(gc_prefix) + "frameset"); - realsense_d435_frameset.define_extern("ion_bb_image_io_realsense_d435_frameset", {}, type_of(), 0); - realsense_d435_frameset.compute_root(); - - // TODO: Seperate channel - Func realsense_d435_infrared(static_cast(gc_prefix) + "output_lr"); - realsense_d435_infrared.define_extern("ion_bb_image_io_realsense_d435_infrared", {realsense_d435_frameset}, {type_of(), type_of()}, 2); - realsense_d435_infrared.compute_root(); - - Func realsense_d435_depth(static_cast(gc_prefix) + "output_d"); - realsense_d435_depth.define_extern("ion_bb_image_io_realsense_d435_depth", {realsense_d435_frameset}, type_of(), 2); - realsense_d435_depth.compute_root(); - - output_l(_) = realsense_d435_infrared(_)[0]; - output_r(_) = realsense_d435_infrared(_)[1]; - output_d = realsense_d435_depth; - } -}; - - -class GenericV4L2Bayer : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "GenericV4L2Bayer"}; - BuildingBlockParam gc_description{"gc_description", "This captures Bayer image from V4L2."}; - BuildingBlockParam gc_tags{"gc_tags", "input,sensor"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height)] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam index{"index", 0}; - BuildingBlockParam url{"url", ""}; - BuildingBlockParam fps{"fps", 20}; - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - BuildingBlockParam bit_width{"bit_width", 10}; - BuildingBlockParam bayer_pattern{"bayer_pattern", BayerMap::Pattern::RGGB, BayerMap::enum_map}; - Output output{"output", Halide::type_of(), 2}; - - void generate() { - using namespace Halide; - - std::string url_str = url; - Halide::Buffer url_buf(url_str.size() + 1); - url_buf.fill(0); - std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); - - std::vector params = { - instance_id++, - cast(index), - cast(fps), - cast(width), - cast(height), - Expr(make_pixel_format(bayer_pattern, bit_width)), - cast(0), - url_buf, - 1.f, 1.f, 1.f, - 0.f, - cast(bit_width), 16 - bit_width + + class IMX219 : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "IMX219" }; + BuildingBlockParam gc_description { "gc_description", "This captures IMX219 image." }; + BuildingBlockParam gc_tags { "gc_tags", "input,sensor" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height)] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam fps { "fps", 24 }; + BuildingBlockParam width { "width", 3264 }; + BuildingBlockParam height { "height", 2464 }; + BuildingBlockParam index { "index", 0 }; + BuildingBlockParam url { "url", "" }; + BuildingBlockParam force_sim_mode { "force_sim_mode", false }; + + Output output { "output", Halide::type_of(), 2 }; + + void generate() + { + using namespace Halide; + std::string url_str = url; + Halide::Buffer url_buf(url_str.size() + 1); + url_buf.fill(0); + std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); + + std::vector params = { + instance_id++, + cast(index), + cast(fps), + cast(width), + cast(height), + cast(Expr(V4L2_PIX_FMT_SRGGB10)), + cast(force_sim_mode), + url_buf, + 0.4f, 0.5f, 0.3125f, + 0.0625f, + 10, 6 + }; + Func v4l2_imx219(static_cast(gc_prefix) + "output"); + v4l2_imx219.define_extern("ion_bb_image_io_v4l2", params, type_of(), 2); + v4l2_imx219.compute_root(); + + output = v4l2_imx219; + } }; - Func v4l2(static_cast(gc_prefix) + "output"); - v4l2.define_extern("ion_bb_image_io_v4l2", params, type_of(), 2); - v4l2.compute_root(); - - output = v4l2; - } -}; - -class CameraSimulation : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "CameraSimulation"}; - BuildingBlockParam gc_description{"gc_description", "This simulates Bayer image."}; - BuildingBlockParam gc_tags{"gc_tags", "input,sensor"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height)] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height,url"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam fps{"fps", 30}; - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - BuildingBlockParam url{"url", ""}; - BuildingBlockParam bayer_pattern{"bayer_pattern", BayerMap::Pattern::RGGB, BayerMap::enum_map}; - BuildingBlockParam bit_width{"bit_width", 10}; - BuildingBlockParam bit_shift{"bit_shift", 0}; - BuildingBlockParam gain_r{"gain_r", 1.f}; - BuildingBlockParam gain_g{"gain_g", 1.f}; - BuildingBlockParam gain_b{"gain_b", 1.f}; - BuildingBlockParam offset{"offset", 0.f}; - - Output output{"output", Halide::type_of(), 2}; - - void generate() { - using namespace Halide; - std::string url_str = url; - Halide::Buffer url_buf(url_str.size() + 1); - url_buf.fill(0); - std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); - - std::vector params = { - instance_id++, - 0, - cast(fps), - cast(width), - cast(height), - Expr(make_pixel_format(bayer_pattern, bit_width)), - cast(1), - url_buf, - cast(gain_r), cast(gain_g), cast(gain_b), - cast(offset), - cast(bit_width), cast(bit_shift) + + class D435 : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "D435" }; + BuildingBlockParam gc_description { "gc_description", "This captures D435 stereo image and depth." }; + BuildingBlockParam gc_tags { "gc_tags", "input,sensor" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output_l: [1280, 720], output_r: [1280, 720], output_d: [1280, 720] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + Output output_l { "output_l", Halide::type_of(), 2 }; + Output output_r { "output_r", Halide::type_of(), 2 }; + Output output_d { "output_d", Halide::type_of(), 2 }; + + void generate() + { + using namespace Halide; + Func realsense_d435_frameset(static_cast(gc_prefix) + "frameset"); + realsense_d435_frameset.define_extern("ion_bb_image_io_realsense_d435_frameset", {}, type_of(), 0); + realsense_d435_frameset.compute_root(); + + // TODO: Seperate channel + Func realsense_d435_infrared(static_cast(gc_prefix) + "output_lr"); + realsense_d435_infrared.define_extern("ion_bb_image_io_realsense_d435_infrared", { realsense_d435_frameset }, { type_of(), type_of() }, 2); + realsense_d435_infrared.compute_root(); + + Func realsense_d435_depth(static_cast(gc_prefix) + "output_d"); + realsense_d435_depth.define_extern("ion_bb_image_io_realsense_d435_depth", { realsense_d435_frameset }, type_of(), 2); + realsense_d435_depth.compute_root(); + + output_l(_) = realsense_d435_infrared(_)[0]; + output_r(_) = realsense_d435_infrared(_)[1]; + output_d = realsense_d435_depth; + } }; - Func camera(static_cast(gc_prefix) + "output"); - camera.define_extern("ion_bb_image_io_v4l2", params, type_of(), 2); - camera.compute_root(); - output = camera; - } -}; -#endif + class GenericV4L2Bayer : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "GenericV4L2Bayer" }; + BuildingBlockParam gc_description { "gc_description", "This captures Bayer image from V4L2." }; + BuildingBlockParam gc_tags { "gc_tags", "input,sensor" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height)] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam index { "index", 0 }; + BuildingBlockParam url { "url", "" }; + BuildingBlockParam fps { "fps", 20 }; + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + BuildingBlockParam bit_width { "bit_width", 10 }; + BuildingBlockParam bayer_pattern { "bayer_pattern", BayerMap::Pattern::RGGB, BayerMap::enum_map }; + Output output { "output", Halide::type_of(), 2 }; + + void generate() + { + using namespace Halide; + + std::string url_str = url; + Halide::Buffer url_buf(url_str.size() + 1); + url_buf.fill(0); + std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); + + std::vector params = { + instance_id++, + cast(index), + cast(fps), + cast(width), + cast(height), + Expr(make_pixel_format(bayer_pattern, bit_width)), + cast(0), + url_buf, + 1.f, 1.f, 1.f, + 0.f, + cast(bit_width), 16 - bit_width + }; + Func v4l2(static_cast(gc_prefix) + "output"); + v4l2.define_extern("ion_bb_image_io_v4l2", params, type_of(), 2); + v4l2.compute_root(); + + output = v4l2; + } + }; + class CameraSimulation : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "CameraSimulation" }; + BuildingBlockParam gc_description { "gc_description", "This simulates Bayer image." }; + BuildingBlockParam gc_tags { "gc_tags", "input,sensor" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height)] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height,url" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam fps { "fps", 30 }; + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + BuildingBlockParam url { "url", "" }; + BuildingBlockParam bayer_pattern { "bayer_pattern", BayerMap::Pattern::RGGB, BayerMap::enum_map }; + BuildingBlockParam bit_width { "bit_width", 10 }; + BuildingBlockParam bit_shift { "bit_shift", 0 }; + BuildingBlockParam gain_r { "gain_r", 1.f }; + BuildingBlockParam gain_g { "gain_g", 1.f }; + BuildingBlockParam gain_b { "gain_b", 1.f }; + BuildingBlockParam offset { "offset", 0.f }; + + Output output { "output", Halide::type_of(), 2 }; + + void generate() + { + using namespace Halide; + std::string url_str = url; + Halide::Buffer url_buf(url_str.size() + 1); + url_buf.fill(0); + std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); + + std::vector params = { + instance_id++, + 0, + cast(fps), + cast(width), + cast(height), + Expr(make_pixel_format(bayer_pattern, bit_width)), + cast(1), + url_buf, + cast(gain_r), cast(gain_g), cast(gain_b), + cast(offset), + cast(bit_width), cast(bit_shift) + }; + Func camera(static_cast(gc_prefix) + "output"); + camera.define_extern("ion_bb_image_io_v4l2", params, type_of(), 2); + camera.compute_root(); + + output = camera; + } + }; +#endif -class GUIDisplay : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "GUI Display"}; - BuildingBlockParam gc_description{"gc_description", "This renders RGB image on GUI window."}; - BuildingBlockParam gc_tags{"gc_tags", "output,display"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam idx{"idx", 0}; - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - Input input{"input", Halide::type_of(), 3}; - Output output{"output", Halide::Int(32), 0}; - - void generate() { - using namespace Halide; - - Func in(static_cast(gc_prefix) + "input"); - Var x, y, c; - in(c, x, y) = mux(c, - {input(x, y, 2), - input(x, y, 1), - input(x, y, 0)}); - in.compute_root(); - if (get_target().has_gpu_feature()) { - Var xo, yo, xi, yi; - in.gpu_tile(x, y, xo, yo, xi, yi, 16, 16); - } else { - in.parallel(y); - } + class GUIDisplay : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "GUI Display" }; + BuildingBlockParam gc_description { "gc_description", "This renders RGB image on GUI window." }; + BuildingBlockParam gc_tags { "gc_tags", "output,display" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam idx { "idx", 0 }; + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + Input input { "input", Halide::type_of(), 3 }; + Output output { "output", Halide::Int(32), 0 }; + + void generate() + { + using namespace Halide; + + Func in(static_cast(gc_prefix) + "input"); + Var x, y, c; + in(c, x, y) = mux(c, + { input(x, y, 2), + input(x, y, 1), + input(x, y, 0) }); + in.compute_root(); + if (get_target().has_gpu_feature()) { + Var xo, yo, xi, yi; + in.gpu_tile(x, y, xo, yo, xi, yi, 16, 16); + } else { + in.parallel(y); + } - std::vector params = {in, static_cast(width), static_cast(height), static_cast(idx)}; - Func display(static_cast(gc_prefix) + "output"); - display.define_extern("ion_bb_image_io_gui_display", params, Int(32), 0); - display.compute_root(); + std::vector params = { in, static_cast(width), static_cast(height), static_cast(idx) }; + Func display(static_cast(gc_prefix) + "output"); + display.define_extern("ion_bb_image_io_gui_display", params, Int(32), 0); + display.compute_root(); - output = display; - } -}; + output = display; + } + }; #ifdef __linux__ -class FBDisplay : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "FBDisplay"}; - BuildingBlockParam gc_description{"gc_description", "This draws image into framebuffer display."}; - BuildingBlockParam gc_tags{"gc_tags", "output,display"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - Input input{"input", Halide::type_of(), 3}; - Output output{"output", Halide::Int(32), 0}; - - void generate() { - using namespace Halide; - - Func in(static_cast(gc_prefix) + "input"); - Var x, y, c; - in(c, x, y) = mux(c, - {input(x, y, 2), - input(x, y, 1), - input(x, y, 0)}); - in.compute_root(); - if (get_target().has_gpu_feature()) { - Var xo, yo, xi, yi; - in.gpu_tile(x, y, xo, yo, xi, yi, 16, 16); - } else { - in.parallel(y); - } + class FBDisplay : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "FBDisplay" }; + BuildingBlockParam gc_description { "gc_description", "This draws image into framebuffer display." }; + BuildingBlockParam gc_tags { "gc_tags", "output,display" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + Input input { "input", Halide::type_of(), 3 }; + Output output { "output", Halide::Int(32), 0 }; + + void generate() + { + using namespace Halide; + + Func in(static_cast(gc_prefix) + "input"); + Var x, y, c; + in(c, x, y) = mux(c, + { input(x, y, 2), + input(x, y, 1), + input(x, y, 0) }); + in.compute_root(); + if (get_target().has_gpu_feature()) { + Var xo, yo, xi, yi; + in.gpu_tile(x, y, xo, yo, xi, yi, 16, 16); + } else { + in.parallel(y); + } - std::vector params = {cast(width), cast(height), in}; - Func display(static_cast(gc_prefix) + "output"); - display.define_extern("ion_bb_image_io_fb_display", params, Halide::type_of(), 0); - display.compute_root(); + std::vector params = { cast(width), cast(height), in }; + Func display(static_cast(gc_prefix) + "output"); + display.define_extern("ion_bb_image_io_fb_display", params, Halide::type_of(), 0); + display.compute_root(); - output = display; - } -}; + output = display; + } + }; #endif -class GrayscaleDataLoader : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "Data Loader / Grayscale"}; - BuildingBlockParam gc_description{"gc_description", "This loads 16-bit grayscale image from specified URL."}; - BuildingBlockParam gc_tags{"gc_tags", "input,imgproc"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height)] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height,url"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - BuildingBlockParam dynamic_range{"dynamic_range", 65535}; - BuildingBlockParam url{"url", ""}; - Output output{"output", Halide::type_of(), 2}; - - void generate() { - using namespace Halide; - - const std::string session_id = sole::uuid4().str(); - Buffer session_id_buf(session_id.size() + 1); - session_id_buf.fill(0); - std::memcpy(session_id_buf.data(), session_id.c_str(), session_id.size()); - - const std::string url_str(url); - Halide::Buffer url_buf(url_str.size() + 1); - url_buf.fill(0); - std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); - - std::vector params = {session_id_buf, url_buf, static_cast(width), static_cast(height), static_cast(dynamic_range)}; - Func grayscale_data_loader(static_cast(gc_prefix) + "output"); - grayscale_data_loader.define_extern("ion_bb_image_io_grayscale_data_loader", params, Halide::type_of(), 2); - grayscale_data_loader.compute_root(); - - output = grayscale_data_loader; - } -}; - -class ColorDataLoader : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "Data Loader / Color"}; - BuildingBlockParam gc_description{"gc_description", "This loads 8-bit/RGB/CHW image from specified URL."}; - BuildingBlockParam gc_tags{"gc_tags", "input,imgproc"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height), 3] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height,url"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - BuildingBlockParam url{"url", ""}; - Output output{"output", Halide::type_of(), 3}; - - void generate() { - using namespace Halide; - - const std::string session_id = sole::uuid4().str(); - Buffer session_id_buf(session_id.size() + 1); - session_id_buf.fill(0); - std::memcpy(session_id_buf.data(), session_id.c_str(), session_id.size()); - - const std::string url_str(url); - Halide::Buffer url_buf(url_str.size() + 1); - url_buf.fill(0); - std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); - - std::vector params = {session_id_buf, url_buf, static_cast(width), static_cast(height)}; - Func color_data_loader(static_cast(gc_prefix) + "output"); - color_data_loader.define_extern("ion_bb_image_io_color_data_loader", params, Halide::type_of(), 3); - color_data_loader.compute_root(); - - output = color_data_loader; - } -}; - - -class ImageSaver : public ion::BuildingBlock { -public: - BuildingBlockParam gc_title{"gc_title", "Image Saver"}; - BuildingBlockParam gc_description{"gc_description", "This saves image to specified path."}; - BuildingBlockParam gc_tags{"gc_tags", "output,imgproc"}; - BuildingBlockParam gc_inference{"gc_inference", R"((function(v){ return { output: [] }}))"}; - BuildingBlockParam gc_mandatory{"gc_mandatory", "width,height"}; - BuildingBlockParam gc_strategy{"gc_strategy", "self"}; - BuildingBlockParam gc_prefix{"gc_prefix", ""}; - - BuildingBlockParam width{"width", 0}; - BuildingBlockParam height{"height", 0}; - BuildingBlockParam path{"path", ""}; - Input input{"input", Halide::type_of(), 3}; - Output output{"output", Halide::Int(32), 0}; - - void generate() { - using namespace Halide; - std::string path_str(path); - Halide::Buffer path_buf(path_str.size() + 1); - path_buf.fill(0); - std::memcpy(path_buf.data(), path_str.c_str(), path_str.size()); - - Func in(static_cast(gc_prefix) + "input"); - Var x, y, c; - in(c, x, y) = mux(c, - {input(x, y, 0), - input(x, y, 1), - input(x, y, 2)}); - in.compute_root(); - if (get_target().has_gpu_feature()) { - Var xo, yo, xi, yi; - in.gpu_tile(x, y, xo, yo, xi, yi, 16, 16); - } else { - in.parallel(y); - } - - std::vector params = {in, static_cast(width), static_cast(height), path_buf}; - Func image_saver(static_cast(gc_prefix) + "output"); - image_saver.define_extern("ion_bb_image_io_image_saver", params, Int(32), 0); - image_saver.compute_root(); - output = image_saver; - } -}; - -template -class U3VCamera1 : public ion::BuildingBlock> { -public: - - BuildingBlockParam frame_sync{"frame_sync", false}; - BuildingBlockParam gain_key_ptr{"gain_key", "Gain"}; - BuildingBlockParam exposure_key_ptr{"exposure_key", "Exposure"}; - BuildingBlockParam realtime_display_mode{"realtime_display_mode", false}; - - Input gain0{ "gain0" }; - Input exposure0{ "exposure0" }; - - Output output0{ "output0", Halide::type_of(), D}; - Output frame_count{ "frame_count", Halide::type_of(), 1 }; - - void generate() { - using namespace Halide; - - Func camera1("u3v_camera1"); - { - Buffer id_buf = this->get_id(); - - const std::string gain_key(gain_key_ptr); - Buffer gain_key_buf(static_cast(gain_key.size() + 1)); - gain_key_buf.fill(0); - std::memcpy(gain_key_buf.data(), gain_key.c_str(), gain_key.size()); - - const std::string exposure_key(exposure_key_ptr); - Buffer exposure_key_buf(static_cast(exposure_key.size() + 1)); - exposure_key_buf.fill(0); - std::memcpy(exposure_key_buf.data(), exposure_key.c_str(), exposure_key.size()); - - std::vector params{ - static_cast(frame_sync), static_cast(realtime_display_mode), - gain0, exposure0, - id_buf, gain_key_buf, exposure_key_buf - }; - camera1.define_extern("ion_bb_image_io_u3v_camera1", params, Halide::type_of(), D); - camera1.compute_root(); - output0(_) = camera1(_); - } - - Func camera1_frame_count; - { - Buffer id_buf = this->get_id(); - camera1_frame_count.define_extern("ion_bb_image_io_u3v_camera1_frame_count",{camera1, 1, static_cast(frame_sync), static_cast(realtime_display_mode), id_buf}, type_of(), 1); - camera1_frame_count.compute_root(); - frame_count(_) = camera1_frame_count(_); - } - - this->register_disposer("u3v_dispose"); - - } -}; - -using U3VCamera1_U8x3 = U3VCamera1; -using U3VCamera1_U8x2 = U3VCamera1; -using U3VCamera1_U16x2 = U3VCamera1; - -template -class U3VCamera2 : public ion::BuildingBlock> { -public: - - BuildingBlockParam frame_sync{"frame_sync", false}; - BuildingBlockParam gain_key_ptr{"gain_key", "Gain"}; - BuildingBlockParam exposure_key_ptr{"exposure_key", "Exposure"}; - BuildingBlockParam realtime_display_mode{"realtime_display_mode", false}; - - Input gain0{ "gain0" }; - Input gain1{ "gain1" }; - Input exposure0{ "exposure0" }; - Input exposure1{ "exposure1" }; - - Output output0{ "output0", Halide::type_of(), D}; - Output output1{ "output1", Halide::type_of(), D}; - Output frame_count{ "frame_count", Halide::type_of(), 1 }; - - void generate() { - using namespace Halide; - - Func camera2("u3v_camera2"); - { - Buffer id_buf = this->get_id(); - - const std::string gain_key(gain_key_ptr); - Buffer gain_key_buf(static_cast(gain_key.size() + 1)); - gain_key_buf.fill(0); - std::memcpy(gain_key_buf.data(), gain_key.c_str(), gain_key.size()); - - const std::string exposure_key(exposure_key_ptr); - Buffer exposure_key_buf(static_cast(exposure_key.size() + 1)); - exposure_key_buf.fill(0); - std::memcpy(exposure_key_buf.data(), exposure_key.c_str(), exposure_key.size()); - - std::vector params{ - static_cast(frame_sync), static_cast(realtime_display_mode), - gain0, gain1, exposure0, exposure1, - id_buf, gain_key_buf, exposure_key_buf - }; - camera2.define_extern("ion_bb_image_io_u3v_camera2", params, { Halide::type_of(), Halide::type_of() }, D); - camera2.compute_root(); - output0(_) = camera2(_)[0]; - output1(_) = camera2(_)[1]; - } - - Func camera2_frame_count;{ - Buffer id_buf = this->get_id(); - camera2_frame_count.define_extern("ion_bb_image_io_u3v_camera2_frame_count", { camera2, 2, static_cast(frame_sync), static_cast(realtime_display_mode), id_buf}, type_of(), 1); - camera2_frame_count.compute_root(); - frame_count(_) = camera2_frame_count(_); - } - this->register_disposer("u3v_dispose"); - } -}; - -using U3VCamera2_U8x3 = U3VCamera2; -using U3VCamera2_U8x2 = U3VCamera2; -using U3VCamera2_U16x2 = U3VCamera2; - -template -class U3VCameraN : public ion::BuildingBlock> { -public: - BuildingBlockParam num_devices{"num_devices", 2}; // NOTE: num_devices refers to sensor count not usb device count - BuildingBlockParam frame_sync{"frame_sync", false}; - BuildingBlockParam realtime_display_mode{"realtime_display_mode", false}; - - BuildingBlockParam enable_control{"enable_control", false}; - BuildingBlockParam gain_key_ptr{"gain_key", "Gain"}; - BuildingBlockParam exposure_key_ptr{"exposure_key", "Exposure"}; - - Output output{ "output", Halide::type_of(), D}; - Output device_info{ "device_info", Halide::type_of(), 1}; - Output frame_count{ "frame_count", Halide::type_of(), 1 }; - - std::vector *> gain; - std::vector *> exposure; - - BuildingBlockParam force_sim_mode{"force_sim_mode", false}; - BuildingBlockParam width{"width", 640}; - BuildingBlockParam height{"height", 480}; - BuildingBlockParam pixel_format{"pixel_format", "Mono8"}; - BuildingBlockParam fps{"fps", 25.0}; - - void configure() { - if (enable_control) { - for (auto i=0; i("gain_" + std::to_string(i))); - exposure.push_back(Halide::Internal::GeneratorBase::add_input("exposure_" + std::to_string(i))); + class GrayscaleDataLoader : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "Data Loader / Grayscale" }; + BuildingBlockParam gc_description { "gc_description", "This loads 16-bit grayscale image from specified URL." }; + BuildingBlockParam gc_tags { "gc_tags", "input,imgproc" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height)] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height,url" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + BuildingBlockParam dynamic_range { "dynamic_range", 65535 }; + BuildingBlockParam url { "url", "" }; + Output output { "output", Halide::type_of(), 2 }; + + void generate() + { + using namespace Halide; + + const std::string session_id = sole::uuid4().str(); + Buffer session_id_buf(session_id.size() + 1); + session_id_buf.fill(0); + std::memcpy(session_id_buf.data(), session_id.c_str(), session_id.size()); + + const std::string url_str(url); + Halide::Buffer url_buf(url_str.size() + 1); + url_buf.fill(0); + std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); + + std::vector params = { session_id_buf, url_buf, static_cast(width), static_cast(height), static_cast(dynamic_range) }; + Func grayscale_data_loader(static_cast(gc_prefix) + "output"); + grayscale_data_loader.define_extern("ion_bb_image_io_grayscale_data_loader", params, Halide::type_of(), 2); + grayscale_data_loader.compute_root(); + + output = grayscale_data_loader; } - } - } - - void generate() { - using namespace Halide; + }; - Func cameraN("u3v_cameraN"); - { - Buffer id_buf = this->get_id(); - - const std::string gain_key(gain_key_ptr); - Buffer gain_key_buf(static_cast(gain_key.size() + 1)); - gain_key_buf.fill(0); - std::memcpy(gain_key_buf.data(), gain_key.c_str(), gain_key.size()); - - const std::string exposure_key(exposure_key_ptr); - Buffer exposure_key_buf(static_cast(exposure_key.size() + 1)); - exposure_key_buf.fill(0); - std::memcpy(exposure_key_buf.data(), exposure_key.c_str(), exposure_key.size()); - - std::string pixel_format_str = pixel_format; - Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); - pixel_format_buf.fill(0); - std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); - - std::vector params{ - id_buf, - static_cast(force_sim_mode), - static_cast(width), static_cast(height),static_cast(fps), - static_cast(frame_sync), static_cast(realtime_display_mode), - static_cast(enable_control), - gain_key_buf, exposure_key_buf, pixel_format_buf - }; + class ColorDataLoader : public ion::BuildingBlock { + public: + BuildingBlockParam gc_title { "gc_title", "Data Loader / Color" }; + BuildingBlockParam gc_description { "gc_description", "This loads 8-bit/RGB/CHW image from specified URL." }; + BuildingBlockParam gc_tags { "gc_tags", "input,imgproc" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [parseInt(v.width), parseInt(v.height), 3] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height,url" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + BuildingBlockParam url { "url", "" }; + Output output { "output", Halide::type_of(), 3 }; + + void generate() + { + using namespace Halide; + + const std::string session_id = sole::uuid4().str(); + Buffer session_id_buf(session_id.size() + 1); + session_id_buf.fill(0); + std::memcpy(session_id_buf.data(), session_id.c_str(), session_id.size()); + + const std::string url_str(url); + Halide::Buffer url_buf(url_str.size() + 1); + url_buf.fill(0); + std::memcpy(url_buf.data(), url_str.c_str(), url_str.size()); + + std::vector params = { session_id_buf, url_buf, static_cast(width), static_cast(height) }; + Func color_data_loader(static_cast(gc_prefix) + "output"); + color_data_loader.define_extern("ion_bb_image_io_color_data_loader", params, Halide::type_of(), 3); + color_data_loader.compute_root(); + + output = color_data_loader; + } + }; - for (int i = 0; i { + public: + BuildingBlockParam gc_title { "gc_title", "Image Saver" }; + BuildingBlockParam gc_description { "gc_description", "This saves image to specified path." }; + BuildingBlockParam gc_tags { "gc_tags", "output,imgproc" }; + BuildingBlockParam gc_inference { "gc_inference", R"((function(v){ return { output: [] }}))" }; + BuildingBlockParam gc_mandatory { "gc_mandatory", "width,height" }; + BuildingBlockParam gc_strategy { "gc_strategy", "self" }; + BuildingBlockParam gc_prefix { "gc_prefix", "" }; + + BuildingBlockParam width { "width", 0 }; + BuildingBlockParam height { "height", 0 }; + BuildingBlockParam path { "path", "" }; + Input input { "input", Halide::type_of(), 3 }; + Output output { "output", Halide::Int(32), 0 }; + + void generate() + { + using namespace Halide; + std::string path_str(path); + Halide::Buffer path_buf(path_str.size() + 1); + path_buf.fill(0); + std::memcpy(path_buf.data(), path_str.c_str(), path_str.size()); + + Func in(static_cast(gc_prefix) + "input"); + Var x, y, c; + in(c, x, y) = mux(c, + { input(x, y, 0), + input(x, y, 1), + input(x, y, 2) }); + in.compute_root(); + if (get_target().has_gpu_feature()) { + Var xo, yo, xi, yi; + in.gpu_tile(x, y, xo, yo, xi, yi, 16, 16); } else { - params.push_back(Internal::make_const(type_of(), 0.0)); + in.parallel(y); } - if (i < exposure.size()) { - params.push_back(*exposure[i]); - } else { - params.push_back(Internal::make_const(type_of(), 0.0)); - } - } - output.resize(num_devices); - cameraN.define_extern("ion_bb_image_io_u3v_multiple_camera" + std::to_string(num_devices), params, std::vector(num_devices, Halide::type_of()), D); - cameraN.compute_root(); - if (num_devices == 1){ - output[0](_) = cameraN(_); - } else { - for (int i = 0; i params = { in, static_cast(width), static_cast(height), path_buf }; + Func image_saver(static_cast(gc_prefix) + "output"); + image_saver.define_extern("ion_bb_image_io_image_saver", params, Int(32), 0); + image_saver.compute_root(); + output = image_saver; } - } - - Func u3v_device_info("u3v_device_info"); - { - - Buffer id_buf = this->get_id(); - std::string pixel_format_str = pixel_format; - Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); - pixel_format_buf.fill(0); - std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); - - std::vector params{ - cameraN, id_buf, static_cast(num_devices), - static_cast(force_sim_mode), - static_cast(width), static_cast(height), static_cast(fps), - static_cast(frame_sync), static_cast(realtime_display_mode), - pixel_format_buf - }; + }; - device_info.resize(num_devices); - std::vector output_type; - for (int i = 0; i < device_info.size(); i++) { - output_type.push_back(Halide::type_of()); - } - u3v_device_info.define_extern("ion_bb_image_io_u3v_device_info" + std::to_string(device_info.size()), params, output_type, 1); - u3v_device_info.compute_root(); - if (device_info.size() == 1){ - device_info[0](_) = u3v_device_info(_); - }else{ - for (int i = 0; i < device_info.size(); i++) { - device_info[i](_) = u3v_device_info(_)[i]; + template + class U3VCamera1 : public ion::BuildingBlock> { + public: + BuildingBlockParam frame_sync { "frame_sync", false }; + BuildingBlockParam gain_key_ptr { "gain_key", "Gain" }; + BuildingBlockParam exposure_key_ptr { "exposure_key", "Exposure" }; + BuildingBlockParam realtime_display_mode { "realtime_display_mode", false }; + + Input gain0 { "gain0" }; + Input exposure0 { "exposure0" }; + + Output output0 { "output0", Halide::type_of(), D }; + Output frame_count { "frame_count", Halide::type_of(), 1 }; + + void generate() + { + using namespace Halide; + + Func camera1("u3v_camera1"); + { + Buffer id_buf = this->get_id(); + + const std::string gain_key(gain_key_ptr); + Buffer gain_key_buf(static_cast(gain_key.size() + 1)); + gain_key_buf.fill(0); + std::memcpy(gain_key_buf.data(), gain_key.c_str(), gain_key.size()); + + const std::string exposure_key(exposure_key_ptr); + Buffer exposure_key_buf(static_cast(exposure_key.size() + 1)); + exposure_key_buf.fill(0); + std::memcpy(exposure_key_buf.data(), exposure_key.c_str(), exposure_key.size()); + + std::vector params { + static_cast(frame_sync), static_cast(realtime_display_mode), + gain0, exposure0, + id_buf, gain_key_buf, exposure_key_buf + }; + camera1.define_extern("ion_bb_image_io_u3v_camera1", params, Halide::type_of(), D); + camera1.compute_root(); + output0(_) = camera1(_); } - } - } - - Func cameraN_fc("u3v_cameraN_fc"); - { - Buffer id_buf = this->get_id(); - std::string pixel_format_str = pixel_format; - Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); - pixel_format_buf.fill(0); - std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); - - std::vector params{ - cameraN, id_buf, static_cast(num_devices), - static_cast(force_sim_mode), - static_cast(width), static_cast(height), static_cast(fps), - static_cast(frame_sync), static_cast(realtime_display_mode), - pixel_format_buf - }; - frame_count.resize(num_devices); - std::vector output_type; - for (int i = 0; i < frame_count.size(); i++) { - output_type.push_back(Halide::type_of()); - } - cameraN_fc.define_extern("ion_bb_image_io_u3v_multiple_camera_frame_count" + std::to_string(output.size()), params, output_type, 1); - cameraN_fc.compute_root(); - if (frame_count.size() == 1){ - frame_count[0](_) = cameraN_fc(_); - }else{ - for (int i = 0; i < device_info.size(); i++) { - frame_count[i](_) = cameraN_fc(_)[i]; + Func camera1_frame_count; + { + Buffer id_buf = this->get_id(); + camera1_frame_count.define_extern("ion_bb_image_io_u3v_camera1_frame_count", { camera1, 1, static_cast(frame_sync), static_cast(realtime_display_mode), id_buf }, type_of(), 1); + camera1_frame_count.compute_root(); + frame_count(_) = camera1_frame_count(_); } - } - } - this->register_disposer("u3v_dispose"); - } - -}; - -using U3VCameraN_U8x3 = U3VCameraN; -using U3VCameraN_U8x2 = U3VCameraN; -using U3VCameraN_U16x2 = U3VCameraN; - -class U3VCameraGenDC : public ion::BuildingBlock { -public: - BuildingBlockParam num_devices{"num_devices", 2}; // NOTE: num_devices refers to sensor count not usb device count - BuildingBlockParam frame_sync{"frame_sync", false}; - BuildingBlockParam realtime_display_mode{"realtime_display_mode", false}; - - BuildingBlockParam enable_control{"enable_control", false}; - BuildingBlockParam gain_key_ptr{"gain_key", "Gain"}; - BuildingBlockParam exposure_key_ptr{"exposure_key", "Exposure"}; - - Output gendc{ "gendc", Halide::type_of(), 1}; - Output device_info{ "device_info", Halide::type_of(), 1}; - - std::vector *> gain; - std::vector *> exposure; - - BuildingBlockParam force_sim_mode{"force_sim_mode", false}; - BuildingBlockParam width{"width", 640}; - BuildingBlockParam height{"height", 480}; - BuildingBlockParam pixel_format{"pixel_format", "Mono8"}; - BuildingBlockParam fps{"fps", 25.0}; - - void configure() { - if (enable_control) { - for (auto i=0; i("gain_" + std::to_string(i))); - exposure.push_back(Halide::Internal::GeneratorBase::add_input("exposure_" + std::to_string(i))); - } - } - } - - void generate() { - using namespace Halide; - Func u3v_gendc("u3v_gendc"); - { - Buffer id_buf = this->get_id(); - - const std::string gain_key(gain_key_ptr); - Buffer gain_key_buf(static_cast(gain_key.size() + 1)); - gain_key_buf.fill(0); - std::memcpy(gain_key_buf.data(), gain_key.c_str(), gain_key.size()); - - const std::string exposure_key(exposure_key_ptr); - Buffer exposure_key_buf(static_cast(exposure_key.size() + 1)); - exposure_key_buf.fill(0); - std::memcpy(exposure_key_buf.data(), exposure_key.c_str(), exposure_key.size()); - - std::string pixel_format_str = pixel_format; - Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); - pixel_format_buf.fill(0); - std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); - - std::vector params{ - id_buf, - static_cast(force_sim_mode), - static_cast(width), static_cast(height),static_cast(fps), - static_cast(frame_sync), static_cast(realtime_display_mode), - static_cast(enable_control), - gain_key_buf, exposure_key_buf, pixel_format_buf - }; + this->register_disposer("u3v_dispose"); + } + }; - for (int i = 0; i(), 0.0)); + using U3VCamera1_U8x3 = U3VCamera1; + using U3VCamera1_U8x2 = U3VCamera1; + using U3VCamera1_U16x2 = U3VCamera1; + + template + class U3VCamera2 : public ion::BuildingBlock> { + public: + BuildingBlockParam frame_sync { "frame_sync", false }; + BuildingBlockParam gain_key_ptr { "gain_key", "Gain" }; + BuildingBlockParam exposure_key_ptr { "exposure_key", "Exposure" }; + BuildingBlockParam realtime_display_mode { "realtime_display_mode", false }; + + Input gain0 { "gain0" }; + Input gain1 { "gain1" }; + Input exposure0 { "exposure0" }; + Input exposure1 { "exposure1" }; + + Output output0 { "output0", Halide::type_of(), D }; + Output output1 { "output1", Halide::type_of(), D }; + Output frame_count { "frame_count", Halide::type_of(), 1 }; + + void generate() + { + using namespace Halide; + + Func camera2("u3v_camera2"); + { + Buffer id_buf = this->get_id(); + + const std::string gain_key(gain_key_ptr); + Buffer gain_key_buf(static_cast(gain_key.size() + 1)); + gain_key_buf.fill(0); + std::memcpy(gain_key_buf.data(), gain_key.c_str(), gain_key.size()); + + const std::string exposure_key(exposure_key_ptr); + Buffer exposure_key_buf(static_cast(exposure_key.size() + 1)); + exposure_key_buf.fill(0); + std::memcpy(exposure_key_buf.data(), exposure_key.c_str(), exposure_key.size()); + + std::vector params { + static_cast(frame_sync), static_cast(realtime_display_mode), + gain0, gain1, exposure0, exposure1, + id_buf, gain_key_buf, exposure_key_buf + }; + camera2.define_extern("ion_bb_image_io_u3v_camera2", params, { Halide::type_of(), Halide::type_of() }, D); + camera2.compute_root(); + output0(_) = camera2(_)[0]; + output1(_) = camera2(_)[1]; } - if (i < exposure.size()) { - params.push_back(*exposure[i]); - } else { - params.push_back(Internal::make_const(type_of(), 0.0)); + + Func camera2_frame_count; + { + Buffer id_buf = this->get_id(); + camera2_frame_count.define_extern("ion_bb_image_io_u3v_camera2_frame_count", { camera2, 2, static_cast(frame_sync), static_cast(realtime_display_mode), id_buf }, type_of(), 1); + camera2_frame_count.compute_root(); + frame_count(_) = camera2_frame_count(_); } + this->register_disposer("u3v_dispose"); } + }; - gendc.resize(num_devices); - std::vector output_type; - for (int i = 0; i < gendc.size(); i++) { - output_type.push_back(Halide::type_of()); - } - u3v_gendc.define_extern("ion_bb_image_io_u3v_gendc_camera" + std::to_string(gendc.size()), params, output_type, 1); - u3v_gendc.compute_root(); - if (gendc.size() == 1){ - gendc[0](_) = u3v_gendc(_); - }else{ - for (int i = 0; i < gendc.size(); i++) { - gendc[i](_) = u3v_gendc(_)[i]; + using U3VCamera2_U8x3 = U3VCamera2; + using U3VCamera2_U8x2 = U3VCamera2; + using U3VCamera2_U16x2 = U3VCamera2; + + template + class U3VCameraN : public ion::BuildingBlock> { + public: + BuildingBlockParam num_devices { "num_devices", 2 }; // NOTE: num_devices refers to sensor count not usb device count + BuildingBlockParam frame_sync { "frame_sync", false }; + BuildingBlockParam realtime_display_mode { "realtime_display_mode", false }; + + BuildingBlockParam enable_control { "enable_control", false }; + BuildingBlockParam gain_key_ptr { "gain_key", "Gain" }; + BuildingBlockParam exposure_key_ptr { "exposure_key", "Exposure" }; + + Output output { "output", Halide::type_of(), D }; + Output device_info { "device_info", Halide::type_of(), 1 }; + Output frame_count { "frame_count", Halide::type_of(), 1 }; + + std::vector*> gain; + std::vector*> exposure; + + BuildingBlockParam force_sim_mode { "force_sim_mode", false }; + BuildingBlockParam width { "width", 640 }; + BuildingBlockParam height { "height", 480 }; + BuildingBlockParam pixel_format { "pixel_format", "Mono8" }; + BuildingBlockParam fps { "fps", 25.0 }; + + void configure() + { + if (enable_control) { + for (auto i = 0; i < num_devices; ++i) { + gain.push_back(Halide::Internal::GeneratorBase::add_input("gain_" + std::to_string(i))); + exposure.push_back(Halide::Internal::GeneratorBase::add_input("exposure_" + std::to_string(i))); + } } } - } - Func u3v_device_info("u3v_device_info"); - { + void generate() + { + using namespace Halide; + + Func cameraN("u3v_cameraN"); + { + Buffer id_buf = this->get_id(); + + const std::string gain_key(gain_key_ptr); + Buffer gain_key_buf(static_cast(gain_key.size() + 1)); + gain_key_buf.fill(0); + std::memcpy(gain_key_buf.data(), gain_key.c_str(), gain_key.size()); + + const std::string exposure_key(exposure_key_ptr); + Buffer exposure_key_buf(static_cast(exposure_key.size() + 1)); + exposure_key_buf.fill(0); + std::memcpy(exposure_key_buf.data(), exposure_key.c_str(), exposure_key.size()); + + std::string pixel_format_str = pixel_format; + Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); + pixel_format_buf.fill(0); + std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); + + std::vector params { + id_buf, + static_cast(force_sim_mode), + static_cast(width), static_cast(height), static_cast(fps), + static_cast(frame_sync), static_cast(realtime_display_mode), + static_cast(enable_control), + gain_key_buf, exposure_key_buf, pixel_format_buf + }; + + for (int i = 0; i < num_devices; i++) { + if (i < gain.size()) { + params.push_back(*gain[i]); + } else { + params.push_back(Internal::make_const(type_of(), 0.0)); + } + if (i < exposure.size()) { + params.push_back(*exposure[i]); + } else { + params.push_back(Internal::make_const(type_of(), 0.0)); + } + } + + output.resize(num_devices); + cameraN.define_extern("ion_bb_image_io_u3v_multiple_camera" + std::to_string(num_devices), params, std::vector(num_devices, Halide::type_of()), D); + cameraN.compute_root(); + if (num_devices == 1) { + output[0](_) = cameraN(_); + } else { + for (int i = 0; i < num_devices; i++) { + output[i](_) = cameraN(_)[i]; + } + } + } - Buffer id_buf = this->get_id(); - std::string pixel_format_str = pixel_format; - Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); - pixel_format_buf.fill(0); - std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); - - std::vector params{ - u3v_gendc, id_buf, static_cast(num_devices), - static_cast(force_sim_mode), - static_cast(width), static_cast(height), static_cast(fps), - static_cast(frame_sync), static_cast(realtime_display_mode), - pixel_format_buf - }; + Func u3v_device_info("u3v_device_info"); + { + + Buffer id_buf = this->get_id(); + std::string pixel_format_str = pixel_format; + Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); + pixel_format_buf.fill(0); + std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); + + std::vector params { + cameraN, id_buf, static_cast(num_devices), + static_cast(force_sim_mode), + static_cast(width), static_cast(height), static_cast(fps), + static_cast(frame_sync), static_cast(realtime_display_mode), + pixel_format_buf + }; + + device_info.resize(num_devices); + std::vector output_type; + for (int i = 0; i < device_info.size(); i++) { + output_type.push_back(Halide::type_of()); + } + u3v_device_info.define_extern("ion_bb_image_io_u3v_device_info" + std::to_string(device_info.size()), params, output_type, 1); + u3v_device_info.compute_root(); + if (device_info.size() == 1) { + device_info[0](_) = u3v_device_info(_); + } else { + for (int i = 0; i < device_info.size(); i++) { + device_info[i](_) = u3v_device_info(_)[i]; + } + } + } - device_info.resize(num_devices); - std::vector output_type; - for (int i = 0; i < device_info.size(); i++) { - output_type.push_back(Halide::type_of()); - } - u3v_device_info.define_extern("ion_bb_image_io_u3v_device_info" + std::to_string(device_info.size()), params, output_type, 1); - u3v_device_info.compute_root(); - if (device_info.size() == 1){ - device_info[0](_) = u3v_device_info(_); - }else{ - for (int i = 0; i < device_info.size(); i++) { - device_info[i](_) = u3v_device_info(_)[i]; + Func cameraN_fc("u3v_cameraN_fc"); + { + Buffer id_buf = this->get_id(); + std::string pixel_format_str = pixel_format; + Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); + pixel_format_buf.fill(0); + std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); + + std::vector params { + cameraN, id_buf, static_cast(num_devices), + static_cast(force_sim_mode), + static_cast(width), static_cast(height), static_cast(fps), + static_cast(frame_sync), static_cast(realtime_display_mode), + pixel_format_buf + }; + + frame_count.resize(num_devices); + std::vector output_type; + for (int i = 0; i < frame_count.size(); i++) { + output_type.push_back(Halide::type_of()); + } + cameraN_fc.define_extern("ion_bb_image_io_u3v_multiple_camera_frame_count" + std::to_string(output.size()), params, output_type, 1); + cameraN_fc.compute_root(); + if (frame_count.size() == 1) { + frame_count[0](_) = cameraN_fc(_); + } else { + for (int i = 0; i < device_info.size(); i++) { + frame_count[i](_) = cameraN_fc(_)[i]; + } + } } + this->register_disposer("u3v_dispose"); } - } - - this->register_disposer("u3v_dispose"); - } -}; + }; + using U3VCameraN_U8x3 = U3VCameraN; + using U3VCameraN_U8x2 = U3VCameraN; + using U3VCameraN_U16x2 = U3VCameraN; + + class U3VCameraGenDC : public ion::BuildingBlock { + public: + BuildingBlockParam num_devices { "num_devices", 2 }; // NOTE: num_devices refers to sensor count not usb device count + BuildingBlockParam frame_sync { "frame_sync", false }; + BuildingBlockParam realtime_display_mode { "realtime_display_mode", false }; + + BuildingBlockParam enable_control { "enable_control", false }; + BuildingBlockParam gain_key_ptr { "gain_key", "Gain" }; + BuildingBlockParam exposure_key_ptr { "exposure_key", "Exposure" }; + + Output gendc { "gendc", Halide::type_of(), 1 }; + Output device_info { "device_info", Halide::type_of(), 1 }; + + std::vector*> gain; + std::vector*> exposure; + + BuildingBlockParam force_sim_mode { "force_sim_mode", false }; + BuildingBlockParam width { "width", 640 }; + BuildingBlockParam height { "height", 480 }; + BuildingBlockParam pixel_format { "pixel_format", "Mono8" }; + BuildingBlockParam fps { "fps", 25.0 }; + + void configure() + { + if (enable_control) { + for (auto i = 0; i < num_devices; ++i) { + gain.push_back(Halide::Internal::GeneratorBase::add_input("gain_" + std::to_string(i))); + exposure.push_back(Halide::Internal::GeneratorBase::add_input("exposure_" + std::to_string(i))); + } + } + } + void generate() + { + using namespace Halide; + + Func u3v_gendc("u3v_gendc"); + { + Buffer id_buf = this->get_id(); + + const std::string gain_key(gain_key_ptr); + Buffer gain_key_buf(static_cast(gain_key.size() + 1)); + gain_key_buf.fill(0); + std::memcpy(gain_key_buf.data(), gain_key.c_str(), gain_key.size()); + + const std::string exposure_key(exposure_key_ptr); + Buffer exposure_key_buf(static_cast(exposure_key.size() + 1)); + exposure_key_buf.fill(0); + std::memcpy(exposure_key_buf.data(), exposure_key.c_str(), exposure_key.size()); + + std::string pixel_format_str = pixel_format; + Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); + pixel_format_buf.fill(0); + std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); + + std::vector params { + id_buf, + static_cast(force_sim_mode), + static_cast(width), static_cast(height), static_cast(fps), + static_cast(frame_sync), static_cast(realtime_display_mode), + static_cast(enable_control), + gain_key_buf, exposure_key_buf, pixel_format_buf + }; + + for (int i = 0; i < num_devices; i++) { + if (i < gain.size()) { + params.push_back(*gain[i]); + } else { + params.push_back(Internal::make_const(type_of(), 0.0)); + } + if (i < exposure.size()) { + params.push_back(*exposure[i]); + } else { + params.push_back(Internal::make_const(type_of(), 0.0)); + } + } + + gendc.resize(num_devices); + std::vector output_type; + for (int i = 0; i < gendc.size(); i++) { + output_type.push_back(Halide::type_of()); + } + u3v_gendc.define_extern("ion_bb_image_io_u3v_gendc_camera" + std::to_string(gendc.size()), params, output_type, 1); + u3v_gendc.compute_root(); + if (gendc.size() == 1) { + gendc[0](_) = u3v_gendc(_); + } else { + for (int i = 0; i < gendc.size(); i++) { + gendc[i](_) = u3v_gendc(_)[i]; + } + } + } -template -class BinarySaver : public ion::BuildingBlock> { -public: - BuildingBlockParam output_directory_ptr{ "output_directory", "." }; - BuildingBlockParam prefix_ptr{"prefix", "raw-"}; + Func u3v_device_info("u3v_device_info"); + { + + Buffer id_buf = this->get_id(); + std::string pixel_format_str = pixel_format; + Halide::Buffer pixel_format_buf(pixel_format_str.size() + 1); + pixel_format_buf.fill(0); + std::memcpy(pixel_format_buf.data(), pixel_format_str.c_str(), pixel_format_str.size()); + + std::vector params { + u3v_gendc, id_buf, static_cast(num_devices), + static_cast(force_sim_mode), + static_cast(width), static_cast(height), static_cast(fps), + static_cast(frame_sync), static_cast(realtime_display_mode), + pixel_format_buf + }; + + device_info.resize(num_devices); + std::vector output_type; + for (int i = 0; i < device_info.size(); i++) { + output_type.push_back(Halide::type_of()); + } + u3v_device_info.define_extern("ion_bb_image_io_u3v_device_info" + std::to_string(device_info.size()), params, output_type, 1); + u3v_device_info.compute_root(); + if (device_info.size() == 1) { + device_info[0](_) = u3v_device_info(_); + } else { + for (int i = 0; i < device_info.size(); i++) { + device_info[i](_) = u3v_device_info(_)[i]; + } + } + } - Input input_images{"input", Halide::type_of(), D}; - Input input_deviceinfo{ "input_deviceinfo", Halide::type_of(), 1 }; - Input frame_count{ "frame_count", Halide::type_of(), 1 }; - Input width{ "width" }; - Input height{ "height" }; + this->register_disposer("u3v_dispose"); + } + }; - Output output{"output"}; + template + class BinarySaver : public ion::BuildingBlock> { + public: + BuildingBlockParam output_directory_ptr { "output_directory", "." }; + BuildingBlockParam prefix_ptr { "prefix", "raw-" }; - void generate() { - using namespace Halide; + Input input_images { "input", Halide::type_of(), D }; + Input input_deviceinfo { "input_deviceinfo", Halide::type_of(), 1 }; + Input frame_count { "frame_count", Halide::type_of(), 1 }; + Input width { "width" }; + Input height { "height" }; - const std::string output_directory(output_directory_ptr); - Halide::Buffer output_directory_buf(static_cast(output_directory.size() + 1)); - output_directory_buf.fill(0); - std::memcpy(output_directory_buf.data(), output_directory.c_str(), output_directory.size()); + Output output { "output" }; - const std::string prefix(prefix_ptr); - Halide::Buffer prefix_buf(static_cast(prefix.size() + 1)); - prefix_buf.fill(0); - std::memcpy(prefix_buf.data(), prefix.c_str(), prefix.size()); + void generate() + { + using namespace Halide; - Func fc; - fc(_) = frame_count(_); - fc.compute_root(); + const std::string output_directory(output_directory_ptr); + Halide::Buffer output_directory_buf(static_cast(output_directory.size() + 1)); + output_directory_buf.fill(0); + std::memcpy(output_directory_buf.data(), output_directory.c_str(), output_directory.size()); - int32_t dim = D; - int32_t byte_depth = sizeof(T); + const std::string prefix(prefix_ptr); + Halide::Buffer prefix_buf(static_cast(prefix.size() + 1)); + prefix_buf.fill(0); + std::memcpy(prefix_buf.data(), prefix.c_str(), prefix.size()); - Buffer id_buf = this->get_id(); + Func fc; + fc(_) = frame_count(_); + fc.compute_root(); - Func image; - image(_) = input_images(_); - image.compute_root(); + int32_t dim = D; + int32_t byte_depth = sizeof(T); - Func deviceinfo; - deviceinfo(_) = input_deviceinfo(_); - deviceinfo.compute_root(); + Buffer id_buf = this->get_id(); - std::vector params = {id_buf, image, deviceinfo, fc, width, height, dim, byte_depth, output_directory_buf, prefix_buf }; - Func ion_bb_image_io_binary_image_saver; - ion_bb_image_io_binary_image_saver.define_extern("ion_bb_image_io_binary_image_saver", params, Int(32), 0); - ion_bb_image_io_binary_image_saver.compute_root(); - output(_) = ion_bb_image_io_binary_image_saver(_); + Func image; + image(_) = input_images(_); + image.compute_root(); - this->register_disposer("writer_dispose"); - } -}; + Func deviceinfo; + deviceinfo(_) = input_deviceinfo(_); + deviceinfo.compute_root(); + std::vector params = { id_buf, image, deviceinfo, fc, width, height, dim, byte_depth, output_directory_buf, prefix_buf }; + Func ion_bb_image_io_binary_image_saver; + ion_bb_image_io_binary_image_saver.define_extern("ion_bb_image_io_binary_image_saver", params, Int(32), 0); + ion_bb_image_io_binary_image_saver.compute_root(); + output(_) = ion_bb_image_io_binary_image_saver(_); -using BinarySaver_U8x3 = BinarySaver; -using BinarySaver_U8x2 = BinarySaver; -using BinarySaver_U16x2 = BinarySaver; + this->register_disposer("writer_dispose"); + } + }; -class BinaryGenDCSaver : public ion::BuildingBlock { -public: - BuildingBlockParam output_directory_ptr{ "output_directory", "." }; + using BinarySaver_U8x3 = BinarySaver; + using BinarySaver_U8x2 = BinarySaver; + using BinarySaver_U16x2 = BinarySaver; - BuildingBlockParam prefix_ptr{"prefix", "raw-"}; + class BinaryGenDCSaver : public ion::BuildingBlock { + public: + BuildingBlockParam output_directory_ptr { "output_directory", "." }; - Input input_gendc{ "input_gendc", Halide::type_of(), 1 }; - Input input_deviceinfo{ "input_deviceinfo", Halide::type_of(), 1 }; + BuildingBlockParam prefix_ptr { "prefix", "raw-" }; + Input input_gendc { "input_gendc", Halide::type_of(), 1 }; + Input input_deviceinfo { "input_deviceinfo", Halide::type_of(), 1 }; - Input payloadsize{ "payloadsize" }; + Input payloadsize { "payloadsize" }; - Output output{ "output" }; + Output output { "output" }; - void generate() { - using namespace Halide; + void generate() + { + using namespace Halide; - const std::string prefix(prefix_ptr); - Halide::Buffer prefix_buf(static_cast(prefix.size() + 1)); - prefix_buf.fill(0); + const std::string prefix(prefix_ptr); + Halide::Buffer prefix_buf(static_cast(prefix.size() + 1)); + prefix_buf.fill(0); - std::memcpy(prefix_buf.data(), prefix.c_str(), prefix.size()); - const std::string output_directory(output_directory_ptr); - Halide::Buffer output_directory_buf(static_cast(output_directory.size() + 1)); - output_directory_buf.fill(0); - std::memcpy(output_directory_buf.data(), output_directory.c_str(), output_directory.size()); - Buffer id_buf = this->get_id(); + std::memcpy(prefix_buf.data(), prefix.c_str(), prefix.size()); + const std::string output_directory(output_directory_ptr); + Halide::Buffer output_directory_buf(static_cast(output_directory.size() + 1)); + output_directory_buf.fill(0); + std::memcpy(output_directory_buf.data(), output_directory.c_str(), output_directory.size()); + Buffer id_buf = this->get_id(); - Func gendc; - gendc(_) = input_gendc(_); - gendc.compute_root(); + Func gendc; + gendc(_) = input_gendc(_); + gendc.compute_root(); - Func deviceinfo; - deviceinfo(_) = input_deviceinfo(_); - deviceinfo.compute_root(); + Func deviceinfo; + deviceinfo(_) = input_deviceinfo(_); + deviceinfo.compute_root(); - std::vector params = { id_buf, gendc, deviceinfo, payloadsize, output_directory_buf, prefix_buf }; - Func image_io_binary_gendc_saver; - image_io_binary_gendc_saver.define_extern("ion_bb_image_io_binary_gendc_saver", params, Int(32), 0); - image_io_binary_gendc_saver.compute_root(); - output() = image_io_binary_gendc_saver(); - - this->register_disposer("writer_dispose"); - } -}; + std::vector params = { id_buf, gendc, deviceinfo, payloadsize, output_directory_buf, prefix_buf }; + Func image_io_binary_gendc_saver; + image_io_binary_gendc_saver.define_extern("ion_bb_image_io_binary_gendc_saver", params, Int(32), 0); + image_io_binary_gendc_saver.compute_root(); + output() = image_io_binary_gendc_saver(); -class BinaryLoader : public ion::BuildingBlock { -public: - BuildingBlockParam output_directory_ptr{ "output_directory_ptr", "" }; - Input width{ "width", 0 }; - Input height{ "height", 0 }; - Output output0{ "output0", UInt(16), 2 }; - Output output1{ "output1", UInt(16), 2 }; - Output finished{ "finished", UInt(1), 1}; - Output bin_idx{ "bin_idx", UInt(32), 1 }; - - void generate() { - using namespace Halide; - - std::string session_id = sole::uuid4().str(); - Buffer session_id_buf(static_cast(session_id.size() + 1)); - session_id_buf.fill(0); - std::memcpy(session_id_buf.data(), session_id.c_str(), session_id.size()); - - const std::string output_directory(output_directory_ptr); - Halide::Buffer output_directory_buf(static_cast(output_directory.size() + 1)); - output_directory_buf.fill(0); - std::memcpy(output_directory_buf.data(), output_directory.c_str(), output_directory.size()); - - std::vector params = { session_id_buf, width, height, output_directory_buf }; - Func binaryloader; - binaryloader.define_extern("binaryloader", params, { UInt(16), UInt(16) }, 2); - binaryloader.compute_root(); - output0(_) = binaryloader(_)[0]; - output1(_) = binaryloader(_)[1]; + this->register_disposer("writer_dispose"); + } + }; + class BinaryLoader : public ion::BuildingBlock { + public: + BuildingBlockParam output_directory_ptr { "output_directory_ptr", "" }; + Input width { "width", 0 }; + Input height { "height", 0 }; + Output output0 { "output0", UInt(16), 2 }; + Output output1 { "output1", UInt(16), 2 }; + Output finished { "finished", UInt(1), 1 }; + Output bin_idx { "bin_idx", UInt(32), 1 }; + + void generate() + { + using namespace Halide; + + std::string session_id = sole::uuid4().str(); + Buffer session_id_buf(static_cast(session_id.size() + 1)); + session_id_buf.fill(0); + std::memcpy(session_id_buf.data(), session_id.c_str(), session_id.size()); + + const std::string output_directory(output_directory_ptr); + Halide::Buffer output_directory_buf(static_cast(output_directory.size() + 1)); + output_directory_buf.fill(0); + std::memcpy(output_directory_buf.data(), output_directory.c_str(), output_directory.size()); + + std::vector params = { session_id_buf, width, height, output_directory_buf }; + Func binaryloader; + binaryloader.define_extern("binaryloader", params, { UInt(16), UInt(16) }, 2); + binaryloader.compute_root(); + output0(_) = binaryloader(_)[0]; + output1(_) = binaryloader(_)[1]; + + Func binaryloader_finished; + binaryloader_finished.define_extern("binaryloader_finished", + { binaryloader, session_id_buf, width, height, output_directory_buf }, + { type_of(), UInt(32) }, 1); + binaryloader_finished.compute_root(); + finished(_) = binaryloader_finished(_)[0]; + bin_idx(_) = binaryloader_finished(_)[1]; + } + }; - Func binaryloader_finished; - binaryloader_finished.define_extern("binaryloader_finished", - { binaryloader, session_id_buf, width, height, output_directory_buf }, - { type_of(), UInt(32)}, 1); - binaryloader_finished.compute_root(); - finished(_) = binaryloader_finished(_)[0]; - bin_idx(_) = binaryloader_finished(_)[1]; - } -}; - -} // namespace image_io -} // namespace bb -} // namespace ion + } // namespace image_io +} // namespace bb +} // namespace ion #ifdef __linux__ ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::IMX219, image_io_imx219); @@ -1281,7 +1274,6 @@ ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::Camera2, image_io_camera2); ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::CameraN, image_io_cameraN); #endif - ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::ColorDataLoader, image_io_color_data_loader); ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::GrayscaleDataLoader, image_io_grayscale_data_loader); @@ -1310,7 +1302,7 @@ ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::BinaryLoader, image_io_binaryload ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::BinaryGenDCSaver, image_io_binary_gendc_saver); -//backward compatability +// backward compatability ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::U3VCamera1_U8x3, u3v_camera1_u8x3); ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::U3VCamera1_U16x2, u3v_camera1_u16x2); ION_REGISTER_BUILDING_BLOCK(ion::bb::image_io::U3VCamera2_U8x3, u3v_camera2_u8x3); diff --git a/src/bb/image-io/halide_image_io.h b/src/bb/image-io/halide_image_io.h index 07ea5c6a..d7882adf 100644 --- a/src/bb/image-io/halide_image_io.h +++ b/src/bb/image-io/halide_image_io.h @@ -31,1906 +31,2074 @@ #include "jpeglib.h" #endif -#include "HalideRuntime.h" // for halide_type_t +#include "HalideRuntime.h" // for halide_type_t namespace Halide { namespace Tools { -struct FormatInfo { - halide_type_t type; - int dimensions; + struct FormatInfo { + halide_type_t type; + int dimensions; - bool operator<(const FormatInfo &other) const { - if (type.code < other.type.code) { - return true; - } else if (type.code > other.type.code) { - return false; - } - if (type.bits < other.type.bits) { - return true; - } else if (type.bits > other.type.bits) { - return false; + bool operator<(const FormatInfo& other) const + { + if (type.code < other.type.code) { + return true; + } else if (type.code > other.type.code) { + return false; + } + if (type.bits < other.type.bits) { + return true; + } else if (type.bits > other.type.bits) { + return false; + } + if (type.lanes < other.type.lanes) { + return true; + } else if (type.lanes > other.type.lanes) { + return false; + } + return (dimensions < other.dimensions); } - if (type.lanes < other.type.lanes) { - return true; - } else if (type.lanes > other.type.lanes) { - return false; + }; + + namespace Internal { + + typedef bool (*CheckFunc)(bool condition, const char* msg); + + inline bool CheckFail(bool condition, const char* msg) + { + if (!condition) { + fprintf(stderr, "%s\n", msg); + abort(); + } + return condition; } - return (dimensions < other.dimensions); - } -}; -namespace Internal { + inline bool CheckReturn(bool condition, const char* msg) + { + return condition; + } -typedef bool (*CheckFunc)(bool condition, const char *msg); + template + To convert(const From& from); -inline bool CheckFail(bool condition, const char *msg) { - if (!condition) { - fprintf(stderr, "%s\n", msg); - abort(); - } - return condition; -} - -inline bool CheckReturn(bool condition, const char *msg) { - return condition; -} - -template -To convert(const From &from); - -// Convert to bool -template<> -inline bool convert(const bool &in) { - return in; -} -template<> -inline bool convert(const uint8_t &in) { - return in != 0; -} -template<> -inline bool convert(const uint16_t &in) { - return in != 0; -} -template<> -inline bool convert(const uint32_t &in) { - return in != 0; -} -template<> -inline bool convert(const uint64_t &in) { - return in != 0; -} -template<> -inline bool convert(const int8_t &in) { - return in != 0; -} -template<> -inline bool convert(const int16_t &in) { - return in != 0; -} -template<> -inline bool convert(const int32_t &in) { - return in != 0; -} -template<> -inline bool convert(const int64_t &in) { - return in != 0; -} -template<> -inline bool convert(const float &in) { - return in != 0; -} -template<> -inline bool convert(const double &in) { - return in != 0; -} - -// Convert to u8 -template<> -inline uint8_t convert(const bool &in) { - return in; -} -template<> -inline uint8_t convert(const uint8_t &in) { - return in; -} -template<> -inline uint8_t convert(const uint16_t &in) { - uint32_t tmp = (uint32_t)(in) + 0x80; - // Fast approximation of div-by-257: see http://research.swtch.com/divmult - return ((tmp * 255 + 255) >> 16); -} -template<> -inline uint8_t convert(const uint32_t &in) { - return (uint8_t)((((uint64_t)in) + 0x00808080) / 0x01010101); -} -// uint64 -> 8 just discards the lower 32 bits: if you were expecting more precision, well, sorry -template<> -inline uint8_t convert(const uint64_t &in) { - return convert(uint32_t(in >> 32)); -} -template<> -inline uint8_t convert(const int8_t &in) { - return convert(in); -} -template<> -inline uint8_t convert(const int16_t &in) { - return convert(in); -} -template<> -inline uint8_t convert(const int32_t &in) { - return convert(in); -} -template<> -inline uint8_t convert(const int64_t &in) { - return convert(in); -} -template<> -inline uint8_t convert(const float &in) { - return (uint8_t)std::lround(in * 255.0f); -} -template<> -inline uint8_t convert(const double &in) { - return (uint8_t)std::lround(in * 255.0); -} - -// Convert to u16 -template<> -inline uint16_t convert(const bool &in) { - return in; -} -template<> -inline uint16_t convert(const uint8_t &in) { - return uint16_t(in) * 0x0101; -} -template<> -inline uint16_t convert(const uint16_t &in) { - return in; -} -template<> -inline uint16_t convert(const uint32_t &in) { - return in >> 16; -} -template<> -inline uint16_t convert(const uint64_t &in) { - return in >> 48; -} -template<> -inline uint16_t convert(const int8_t &in) { - return convert(in); -} -template<> -inline uint16_t convert(const int16_t &in) { - return convert(in); -} -template<> -inline uint16_t convert(const int32_t &in) { - return convert(in); -} -template<> -inline uint16_t convert(const int64_t &in) { - return convert(in); -} -template<> -inline uint16_t convert(const float &in) { - return (uint16_t)std::lround(in * 65535.0f); -} -template<> -inline uint16_t convert(const double &in) { - return (uint16_t)std::lround(in * 65535.0); -} - -// Convert to u32 -template<> -inline uint32_t convert(const bool &in) { - return in; -} -template<> -inline uint32_t convert(const uint8_t &in) { - return uint32_t(in) * 0x01010101; -} -template<> -inline uint32_t convert(const uint16_t &in) { - return uint32_t(in) * 0x00010001; -} -template<> -inline uint32_t convert(const uint32_t &in) { - return in; -} -template<> -inline uint32_t convert(const uint64_t &in) { - return (uint32_t)(in >> 32); -} -template<> -inline uint32_t convert(const int8_t &in) { - return convert(in); -} -template<> -inline uint32_t convert(const int16_t &in) { - return convert(in); -} -template<> -inline uint32_t convert(const int32_t &in) { - return convert(in); -} -template<> -inline uint32_t convert(const int64_t &in) { - return convert(in); -} -template<> -inline uint32_t convert(const float &in) { - return (uint32_t)std::llround(in * 4294967295.0); -} -template<> -inline uint32_t convert(const double &in) { - return (uint32_t)std::llround(in * 4294967295.0); -} - -// Convert to u64 -template<> -inline uint64_t convert(const bool &in) { - return in; -} -template<> -inline uint64_t convert(const uint8_t &in) { - return uint64_t(in) * 0x0101010101010101LL; -} -template<> -inline uint64_t convert(const uint16_t &in) { - return uint64_t(in) * 0x0001000100010001LL; -} -template<> -inline uint64_t convert(const uint32_t &in) { - return uint64_t(in) * 0x0000000100000001LL; -} -template<> -inline uint64_t convert(const uint64_t &in) { - return in; -} -template<> -inline uint64_t convert(const int8_t &in) { - return convert(in); -} -template<> -inline uint64_t convert(const int16_t &in) { - return convert(in); -} -template<> -inline uint64_t convert(const int32_t &in) { - return convert(in); -} -template<> -inline uint64_t convert(const int64_t &in) { - return convert(in); -} -template<> -inline uint64_t convert(const float &in) { - return convert((uint32_t)std::llround(in * 4294967295.0)); -} -template<> -inline uint64_t convert(const double &in) { - return convert((uint32_t)std::llround(in * 4294967295.0)); -} - -// Convert to i8 -template<> -inline int8_t convert(const bool &in) { - return in; -} -template<> -inline int8_t convert(const uint8_t &in) { - return convert(in); -} -template<> -inline int8_t convert(const uint16_t &in) { - return convert(in); -} -template<> -inline int8_t convert(const uint32_t &in) { - return convert(in); -} -template<> -inline int8_t convert(const uint64_t &in) { - return convert(in); -} -template<> -inline int8_t convert(const int8_t &in) { - return convert(in); -} -template<> -inline int8_t convert(const int16_t &in) { - return convert(in); -} -template<> -inline int8_t convert(const int32_t &in) { - return convert(in); -} -template<> -inline int8_t convert(const int64_t &in) { - return convert(in); -} -template<> -inline int8_t convert(const float &in) { - return convert(in); -} -template<> -inline int8_t convert(const double &in) { - return convert(in); -} - -// Convert to i16 -template<> -inline int16_t convert(const bool &in) { - return in; -} -template<> -inline int16_t convert(const uint8_t &in) { - return convert(in); -} -template<> -inline int16_t convert(const uint16_t &in) { - return convert(in); -} -template<> -inline int16_t convert(const uint32_t &in) { - return convert(in); -} -template<> -inline int16_t convert(const uint64_t &in) { - return convert(in); -} -template<> -inline int16_t convert(const int8_t &in) { - return convert(in); -} -template<> -inline int16_t convert(const int16_t &in) { - return convert(in); -} -template<> -inline int16_t convert(const int32_t &in) { - return convert(in); -} -template<> -inline int16_t convert(const int64_t &in) { - return convert(in); -} -template<> -inline int16_t convert(const float &in) { - return convert(in); -} -template<> -inline int16_t convert(const double &in) { - return convert(in); -} - -// Convert to i32 -template<> -inline int32_t convert(const bool &in) { - return in; -} -template<> -inline int32_t convert(const uint8_t &in) { - return convert(in); -} -template<> -inline int32_t convert(const uint16_t &in) { - return convert(in); -} -template<> -inline int32_t convert(const uint32_t &in) { - return convert(in); -} -template<> -inline int32_t convert(const uint64_t &in) { - return convert(in); -} -template<> -inline int32_t convert(const int8_t &in) { - return convert(in); -} -template<> -inline int32_t convert(const int16_t &in) { - return convert(in); -} -template<> -inline int32_t convert(const int32_t &in) { - return convert(in); -} -template<> -inline int32_t convert(const int64_t &in) { - return convert(in); -} -template<> -inline int32_t convert(const float &in) { - return convert(in); -} -template<> -inline int32_t convert(const double &in) { - return convert(in); -} - -// Convert to i64 -template<> -inline int64_t convert(const bool &in) { - return in; -} -template<> -inline int64_t convert(const uint8_t &in) { - return convert(in); -} -template<> -inline int64_t convert(const uint16_t &in) { - return convert(in); -} -template<> -inline int64_t convert(const uint32_t &in) { - return convert(in); -} -template<> -inline int64_t convert(const uint64_t &in) { - return convert(in); -} -template<> -inline int64_t convert(const int8_t &in) { - return convert(in); -} -template<> -inline int64_t convert(const int16_t &in) { - return convert(in); -} -template<> -inline int64_t convert(const int32_t &in) { - return convert(in); -} -template<> -inline int64_t convert(const int64_t &in) { - return convert(in); -} -template<> -inline int64_t convert(const float &in) { - return convert(in); -} -template<> -inline int64_t convert(const double &in) { - return convert(in); -} - -// Convert to f32 -template<> -inline float convert(const bool &in) { - return in; -} -template<> -inline float convert(const uint8_t &in) { - return in / 255.0f; -} -template<> -inline float convert(const uint16_t &in) { - return in / 65535.0f; -} -template<> -inline float convert(const uint32_t &in) { - return (float)(in / 4294967295.0); -} -template<> -inline float convert(const uint64_t &in) { - return convert(uint32_t(in >> 32)); -} -template<> -inline float convert(const int8_t &in) { - return convert(in); -} -template<> -inline float convert(const int16_t &in) { - return convert(in); -} -template<> -inline float convert(const int32_t &in) { - return convert(in); -} -template<> -inline float convert(const int64_t &in) { - return convert(in); -} -template<> -inline float convert(const float &in) { - return in; -} -template<> -inline float convert(const double &in) { - return (float)in; -} - -// Convert to f64 -template<> -inline double convert(const bool &in) { - return in; -} -template<> -inline double convert(const uint8_t &in) { - return in / 255.0f; -} -template<> -inline double convert(const uint16_t &in) { - return in / 65535.0f; -} -template<> -inline double convert(const uint32_t &in) { - return (double)(in / 4294967295.0); -} -template<> -inline double convert(const uint64_t &in) { - return convert(uint32_t(in >> 32)); -} -template<> -inline double convert(const int8_t &in) { - return convert(in); -} -template<> -inline double convert(const int16_t &in) { - return convert(in); -} -template<> -inline double convert(const int32_t &in) { - return convert(in); -} -template<> -inline double convert(const int64_t &in) { - return convert(in); -} -template<> -inline double convert(const float &in) { - return (double)in; -} -template<> -inline double convert(const double &in) { - return in; -} - -inline std::string to_lowercase(const std::string &s) { - std::string r = s; - std::transform(r.begin(), r.end(), r.begin(), ::tolower); - return r; -} - -inline std::string get_lowercase_extension(const std::string &path) { - size_t last_dot = path.rfind('.'); - if (last_dot == std::string::npos) { - return ""; - } - return to_lowercase(path.substr(last_dot + 1)); -} - -template -ElemType read_big_endian(const uint8_t *src); - -template<> -inline uint8_t read_big_endian(const uint8_t *src) { - return *src; -} - -template<> -inline uint16_t read_big_endian(const uint8_t *src) { - return (((uint16_t)src[0]) << 8) | ((uint16_t)src[1]); -} - -template -void write_big_endian(const ElemType &src, uint8_t *dst); - -template<> -inline void write_big_endian(const uint8_t &src, uint8_t *dst) { - *dst = src; -} - -template<> -inline void write_big_endian(const uint16_t &src, uint8_t *dst) { - dst[0] = src >> 8; - dst[1] = src & 0xff; -} - -struct FileOpener { - FileOpener(const std::string &filename, const char *mode) - : f(fopen(filename.c_str(), mode)) { - // nothing - } + // Convert to bool + template <> + inline bool convert(const bool& in) + { + return in; + } + template <> + inline bool convert(const uint8_t& in) + { + return in != 0; + } + template <> + inline bool convert(const uint16_t& in) + { + return in != 0; + } + template <> + inline bool convert(const uint32_t& in) + { + return in != 0; + } + template <> + inline bool convert(const uint64_t& in) + { + return in != 0; + } + template <> + inline bool convert(const int8_t& in) + { + return in != 0; + } + template <> + inline bool convert(const int16_t& in) + { + return in != 0; + } + template <> + inline bool convert(const int32_t& in) + { + return in != 0; + } + template <> + inline bool convert(const int64_t& in) + { + return in != 0; + } + template <> + inline bool convert(const float& in) + { + return in != 0; + } + template <> + inline bool convert(const double& in) + { + return in != 0; + } - ~FileOpener() { - if (f != nullptr) { - fclose(f); + // Convert to u8 + template <> + inline uint8_t convert(const bool& in) + { + return in; + } + template <> + inline uint8_t convert(const uint8_t& in) + { + return in; + } + template <> + inline uint8_t convert(const uint16_t& in) + { + uint32_t tmp = (uint32_t)(in) + 0x80; + // Fast approximation of div-by-257: see http://research.swtch.com/divmult + return ((tmp * 255 + 255) >> 16); + } + template <> + inline uint8_t convert(const uint32_t& in) + { + return (uint8_t)((((uint64_t)in) + 0x00808080) / 0x01010101); + } + // uint64 -> 8 just discards the lower 32 bits: if you were expecting more precision, well, sorry + template <> + inline uint8_t convert(const uint64_t& in) + { + return convert(uint32_t(in >> 32)); + } + template <> + inline uint8_t convert(const int8_t& in) + { + return convert(in); + } + template <> + inline uint8_t convert(const int16_t& in) + { + return convert(in); + } + template <> + inline uint8_t convert(const int32_t& in) + { + return convert(in); + } + template <> + inline uint8_t convert(const int64_t& in) + { + return convert(in); + } + template <> + inline uint8_t convert(const float& in) + { + return (uint8_t)std::lround(in * 255.0f); + } + template <> + inline uint8_t convert(const double& in) + { + return (uint8_t)std::lround(in * 255.0); } - } - // read a line of data, skipping lines that begin with '#" - char *read_line(char *buf, int maxlen) { - char *status; - do { - status = fgets(buf, maxlen, f); - } while (status && buf[0] == '#'); - return (status); - } + // Convert to u16 + template <> + inline uint16_t convert(const bool& in) + { + return in; + } + template <> + inline uint16_t convert(const uint8_t& in) + { + return uint16_t(in) * 0x0101; + } + template <> + inline uint16_t convert(const uint16_t& in) + { + return in; + } + template <> + inline uint16_t convert(const uint32_t& in) + { + return in >> 16; + } + template <> + inline uint16_t convert(const uint64_t& in) + { + return in >> 48; + } + template <> + inline uint16_t convert(const int8_t& in) + { + return convert(in); + } + template <> + inline uint16_t convert(const int16_t& in) + { + return convert(in); + } + template <> + inline uint16_t convert(const int32_t& in) + { + return convert(in); + } + template <> + inline uint16_t convert(const int64_t& in) + { + return convert(in); + } + template <> + inline uint16_t convert(const float& in) + { + return (uint16_t)std::lround(in * 65535.0f); + } + template <> + inline uint16_t convert(const double& in) + { + return (uint16_t)std::lround(in * 65535.0); + } - // call read_line and to a sscanf() on it - int scan_line(const char *fmt, ...) { - char buf[1024]; - if (!read_line(buf, 1024)) { - return 0; - } - va_list args; - va_start(args, fmt); - int result = vsscanf(buf, fmt, args); - va_end(args); - return result; - } + // Convert to u32 + template <> + inline uint32_t convert(const bool& in) + { + return in; + } + template <> + inline uint32_t convert(const uint8_t& in) + { + return uint32_t(in) * 0x01010101; + } + template <> + inline uint32_t convert(const uint16_t& in) + { + return uint32_t(in) * 0x00010001; + } + template <> + inline uint32_t convert(const uint32_t& in) + { + return in; + } + template <> + inline uint32_t convert(const uint64_t& in) + { + return (uint32_t)(in >> 32); + } + template <> + inline uint32_t convert(const int8_t& in) + { + return convert(in); + } + template <> + inline uint32_t convert(const int16_t& in) + { + return convert(in); + } + template <> + inline uint32_t convert(const int32_t& in) + { + return convert(in); + } + template <> + inline uint32_t convert(const int64_t& in) + { + return convert(in); + } + template <> + inline uint32_t convert(const float& in) + { + return (uint32_t)std::llround(in * 4294967295.0); + } + template <> + inline uint32_t convert(const double& in) + { + return (uint32_t)std::llround(in * 4294967295.0); + } - bool read_bytes(void *data, size_t count) { - return fread(data, 1, count, f) == count; - } + // Convert to u64 + template <> + inline uint64_t convert(const bool& in) + { + return in; + } + template <> + inline uint64_t convert(const uint8_t& in) + { + return uint64_t(in) * 0x0101010101010101LL; + } + template <> + inline uint64_t convert(const uint16_t& in) + { + return uint64_t(in) * 0x0001000100010001LL; + } + template <> + inline uint64_t convert(const uint32_t& in) + { + return uint64_t(in) * 0x0000000100000001LL; + } + template <> + inline uint64_t convert(const uint64_t& in) + { + return in; + } + template <> + inline uint64_t convert(const int8_t& in) + { + return convert(in); + } + template <> + inline uint64_t convert(const int16_t& in) + { + return convert(in); + } + template <> + inline uint64_t convert(const int32_t& in) + { + return convert(in); + } + template <> + inline uint64_t convert(const int64_t& in) + { + return convert(in); + } + template <> + inline uint64_t convert(const float& in) + { + return convert((uint32_t)std::llround(in * 4294967295.0)); + } + template <> + inline uint64_t convert(const double& in) + { + return convert((uint32_t)std::llround(in * 4294967295.0)); + } - template - bool read_array(T (&data)[N]) { - return read_bytes(&data[0], sizeof(T) * N); - } + // Convert to i8 + template <> + inline int8_t convert(const bool& in) + { + return in; + } + template <> + inline int8_t convert(const uint8_t& in) + { + return convert(in); + } + template <> + inline int8_t convert(const uint16_t& in) + { + return convert(in); + } + template <> + inline int8_t convert(const uint32_t& in) + { + return convert(in); + } + template <> + inline int8_t convert(const uint64_t& in) + { + return convert(in); + } + template <> + inline int8_t convert(const int8_t& in) + { + return convert(in); + } + template <> + inline int8_t convert(const int16_t& in) + { + return convert(in); + } + template <> + inline int8_t convert(const int32_t& in) + { + return convert(in); + } + template <> + inline int8_t convert(const int64_t& in) + { + return convert(in); + } + template <> + inline int8_t convert(const float& in) + { + return convert(in); + } + template <> + inline int8_t convert(const double& in) + { + return convert(in); + } - template - bool read_vector(std::vector *v) { - return read_bytes(v->data(), v->size() * sizeof(T)); - } + // Convert to i16 + template <> + inline int16_t convert(const bool& in) + { + return in; + } + template <> + inline int16_t convert(const uint8_t& in) + { + return convert(in); + } + template <> + inline int16_t convert(const uint16_t& in) + { + return convert(in); + } + template <> + inline int16_t convert(const uint32_t& in) + { + return convert(in); + } + template <> + inline int16_t convert(const uint64_t& in) + { + return convert(in); + } + template <> + inline int16_t convert(const int8_t& in) + { + return convert(in); + } + template <> + inline int16_t convert(const int16_t& in) + { + return convert(in); + } + template <> + inline int16_t convert(const int32_t& in) + { + return convert(in); + } + template <> + inline int16_t convert(const int64_t& in) + { + return convert(in); + } + template <> + inline int16_t convert(const float& in) + { + return convert(in); + } + template <> + inline int16_t convert(const double& in) + { + return convert(in); + } - bool write_bytes(const void *data, size_t count) { - return fwrite(data, 1, count, f) == count; - } + // Convert to i32 + template <> + inline int32_t convert(const bool& in) + { + return in; + } + template <> + inline int32_t convert(const uint8_t& in) + { + return convert(in); + } + template <> + inline int32_t convert(const uint16_t& in) + { + return convert(in); + } + template <> + inline int32_t convert(const uint32_t& in) + { + return convert(in); + } + template <> + inline int32_t convert(const uint64_t& in) + { + return convert(in); + } + template <> + inline int32_t convert(const int8_t& in) + { + return convert(in); + } + template <> + inline int32_t convert(const int16_t& in) + { + return convert(in); + } + template <> + inline int32_t convert(const int32_t& in) + { + return convert(in); + } + template <> + inline int32_t convert(const int64_t& in) + { + return convert(in); + } + template <> + inline int32_t convert(const float& in) + { + return convert(in); + } + template <> + inline int32_t convert(const double& in) + { + return convert(in); + } - template - bool write_vector(const std::vector &v) { - return write_bytes(v.data(), v.size() * sizeof(T)); - } + // Convert to i64 + template <> + inline int64_t convert(const bool& in) + { + return in; + } + template <> + inline int64_t convert(const uint8_t& in) + { + return convert(in); + } + template <> + inline int64_t convert(const uint16_t& in) + { + return convert(in); + } + template <> + inline int64_t convert(const uint32_t& in) + { + return convert(in); + } + template <> + inline int64_t convert(const uint64_t& in) + { + return convert(in); + } + template <> + inline int64_t convert(const int8_t& in) + { + return convert(in); + } + template <> + inline int64_t convert(const int16_t& in) + { + return convert(in); + } + template <> + inline int64_t convert(const int32_t& in) + { + return convert(in); + } + template <> + inline int64_t convert(const int64_t& in) + { + return convert(in); + } + template <> + inline int64_t convert(const float& in) + { + return convert(in); + } + template <> + inline int64_t convert(const double& in) + { + return convert(in); + } - template - bool write_array(const T (&data)[N]) { - return write_bytes(&data[0], sizeof(T) * N); - } + // Convert to f32 + template <> + inline float convert(const bool& in) + { + return in; + } + template <> + inline float convert(const uint8_t& in) + { + return in / 255.0f; + } + template <> + inline float convert(const uint16_t& in) + { + return in / 65535.0f; + } + template <> + inline float convert(const uint32_t& in) + { + return (float)(in / 4294967295.0); + } + template <> + inline float convert(const uint64_t& in) + { + return convert(uint32_t(in >> 32)); + } + template <> + inline float convert(const int8_t& in) + { + return convert(in); + } + template <> + inline float convert(const int16_t& in) + { + return convert(in); + } + template <> + inline float convert(const int32_t& in) + { + return convert(in); + } + template <> + inline float convert(const int64_t& in) + { + return convert(in); + } + template <> + inline float convert(const float& in) + { + return in; + } + template <> + inline float convert(const double& in) + { + return (float)in; + } - FILE *const f; -}; + // Convert to f64 + template <> + inline double convert(const bool& in) + { + return in; + } + template <> + inline double convert(const uint8_t& in) + { + return in / 255.0f; + } + template <> + inline double convert(const uint16_t& in) + { + return in / 65535.0f; + } + template <> + inline double convert(const uint32_t& in) + { + return (double)(in / 4294967295.0); + } + template <> + inline double convert(const uint64_t& in) + { + return convert(uint32_t(in >> 32)); + } + template <> + inline double convert(const int8_t& in) + { + return convert(in); + } + template <> + inline double convert(const int16_t& in) + { + return convert(in); + } + template <> + inline double convert(const int32_t& in) + { + return convert(in); + } + template <> + inline double convert(const int64_t& in) + { + return convert(in); + } + template <> + inline double convert(const float& in) + { + return (double)in; + } + template <> + inline double convert(const double& in) + { + return in; + } -constexpr int AnyDims = -1; + inline std::string to_lowercase(const std::string& s) + { + std::string r = s; + std::transform(r.begin(), r.end(), r.begin(), ::tolower); + return r; + } -// Read a row of ElemTypes from a byte buffer and copy them into a specific image row. -// Multibyte elements are assumed to be big-endian. -template -void read_big_endian_row(const uint8_t *src, int y, ImageType *im) { - auto im_typed = im->template as(); - const int xmin = im_typed.dim(0).min(); - const int xmax = im_typed.dim(0).max(); - if (im_typed.dimensions() > 2) { - const int cmin = im_typed.dim(2).min(); - const int cmax = im_typed.dim(2).max(); - for (int x = xmin; x <= xmax; x++) { - for (int c = cmin; c <= cmax; c++) { - im_typed(x, y, c + cmin) = read_big_endian(src); - src += sizeof(ElemType); + inline std::string get_lowercase_extension(const std::string& path) + { + size_t last_dot = path.rfind('.'); + if (last_dot == std::string::npos) { + return ""; } + return to_lowercase(path.substr(last_dot + 1)); } - } else { - for (int x = xmin; x <= xmax; x++) { - im_typed(x, y) = read_big_endian(src); - src += sizeof(ElemType); + + template + ElemType read_big_endian(const uint8_t* src); + + template <> + inline uint8_t read_big_endian(const uint8_t* src) + { + return *src; } - } -} - -// Copy a row from an image into a byte buffer. -// Multibyte elements are written in big-endian layout. -template -void write_big_endian_row(const ImageType &im, int y, uint8_t *dst) { - auto im_typed = im.template as::type, AnyDims>(); - const int xmin = im_typed.dim(0).min(); - const int xmax = im_typed.dim(0).max(); - if (im_typed.dimensions() > 2) { - const int cmin = im_typed.dim(2).min(); - const int cmax = im_typed.dim(2).max(); - for (int x = xmin; x <= xmax; x++) { - for (int c = cmin; c <= cmax; c++) { - write_big_endian(im_typed(x, y, c), dst); - dst += sizeof(ElemType); - } - } - } else { - for (int x = xmin; x <= xmax; x++) { - write_big_endian(im_typed(x, y), dst); - dst += sizeof(ElemType); + + template <> + inline uint16_t read_big_endian(const uint8_t* src) + { + return (((uint16_t)src[0]) << 8) | ((uint16_t)src[1]); + } + + template + void write_big_endian(const ElemType& src, uint8_t* dst); + + template <> + inline void write_big_endian(const uint8_t& src, uint8_t* dst) + { + *dst = src; + } + + template <> + inline void write_big_endian(const uint16_t& src, uint8_t* dst) + { + dst[0] = src >> 8; + dst[1] = src & 0xff; + } + + struct FileOpener { + FileOpener(const std::string& filename, const char* mode) + : f(fopen(filename.c_str(), mode)) + { + // nothing + } + + ~FileOpener() + { + if (f != nullptr) { + fclose(f); + } + } + + // read a line of data, skipping lines that begin with '#" + char* read_line(char* buf, int maxlen) + { + char* status; + do { + status = fgets(buf, maxlen, f); + } while (status && buf[0] == '#'); + return (status); + } + + // call read_line and to a sscanf() on it + int scan_line(const char* fmt, ...) + { + char buf[1024]; + if (!read_line(buf, 1024)) { + return 0; + } + va_list args; + va_start(args, fmt); + int result = vsscanf(buf, fmt, args); + va_end(args); + return result; + } + + bool read_bytes(void* data, size_t count) + { + return fread(data, 1, count, f) == count; + } + + template + bool read_array(T (&data)[N]) + { + return read_bytes(&data[0], sizeof(T) * N); + } + + template + bool read_vector(std::vector* v) + { + return read_bytes(v->data(), v->size() * sizeof(T)); + } + + bool write_bytes(const void* data, size_t count) + { + return fwrite(data, 1, count, f) == count; + } + + template + bool write_vector(const std::vector& v) + { + return write_bytes(v.data(), v.size() * sizeof(T)); + } + + template + bool write_array(const T (&data)[N]) + { + return write_bytes(&data[0], sizeof(T) * N); + } + + FILE* const f; + }; + + constexpr int AnyDims = -1; + + // Read a row of ElemTypes from a byte buffer and copy them into a specific image row. + // Multibyte elements are assumed to be big-endian. + template + void read_big_endian_row(const uint8_t* src, int y, ImageType* im) + { + auto im_typed = im->template as(); + const int xmin = im_typed.dim(0).min(); + const int xmax = im_typed.dim(0).max(); + if (im_typed.dimensions() > 2) { + const int cmin = im_typed.dim(2).min(); + const int cmax = im_typed.dim(2).max(); + for (int x = xmin; x <= xmax; x++) { + for (int c = cmin; c <= cmax; c++) { + im_typed(x, y, c + cmin) = read_big_endian(src); + src += sizeof(ElemType); + } + } + } else { + for (int x = xmin; x <= xmax; x++) { + im_typed(x, y) = read_big_endian(src); + src += sizeof(ElemType); + } + } + } + + // Copy a row from an image into a byte buffer. + // Multibyte elements are written in big-endian layout. + template + void write_big_endian_row(const ImageType& im, int y, uint8_t* dst) + { + auto im_typed = im.template as::type, AnyDims>(); + const int xmin = im_typed.dim(0).min(); + const int xmax = im_typed.dim(0).max(); + if (im_typed.dimensions() > 2) { + const int cmin = im_typed.dim(2).min(); + const int cmax = im_typed.dim(2).max(); + for (int x = xmin; x <= xmax; x++) { + for (int c = cmin; c <= cmax; c++) { + write_big_endian(im_typed(x, y, c), dst); + dst += sizeof(ElemType); + } + } + } else { + for (int x = xmin; x <= xmax; x++) { + write_big_endian(im_typed(x, y), dst); + dst += sizeof(ElemType); + } + } } - } -} #ifndef HALIDE_NO_PNG -template -bool load_png(const std::string &filename, ImageType *im) { - static_assert(!ImageType::has_static_halide_type, ""); + template + bool load_png(const std::string& filename, ImageType* im) + { + static_assert(!ImageType::has_static_halide_type, ""); - /* open file and test for it being a png */ - Internal::FileOpener f(filename, "rb"); - if (!check(f.f != nullptr, "File could not be opened for reading")) { - return false; - } - png_byte header[8]; - if (!check(f.read_array(header), "File ended before end of header")) { - return false; - } - if (!check(!png_sig_cmp(header, 0, 8), "File is not recognized as a PNG file")) { - return false; - } + /* open file and test for it being a png */ + Internal::FileOpener f(filename, "rb"); + if (!check(f.f != nullptr, "File could not be opened for reading")) { + return false; + } + png_byte header[8]; + if (!check(f.read_array(header), "File ended before end of header")) { + return false; + } + if (!check(!png_sig_cmp(header, 0, 8), "File is not recognized as a PNG file")) { + return false; + } - /* initialize stuff */ - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (!check(png_ptr != nullptr, "png_create_read_struct failed")) { - return false; - } + /* initialize stuff */ + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!check(png_ptr != nullptr, "png_create_read_struct failed")) { + return false; + } - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!check(info_ptr != nullptr, "png_create_info_struct failed")) { - return false; - } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!check(info_ptr != nullptr, "png_create_info_struct failed")) { + return false; + } - if (!check(!setjmp(png_jmpbuf(png_ptr)), "Error loading PNG")) { - return false; - } + if (!check(!setjmp(png_jmpbuf(png_ptr)), "Error loading PNG")) { + return false; + } - png_init_io(png_ptr, f.f); - png_set_sig_bytes(png_ptr, 8); + png_init_io(png_ptr, f.f); + png_set_sig_bytes(png_ptr, 8); - png_read_info(png_ptr, info_ptr); + png_read_info(png_ptr, info_ptr); - const int width = png_get_image_width(png_ptr, info_ptr); - const int height = png_get_image_height(png_ptr, info_ptr); - const int channels = png_get_channels(png_ptr, info_ptr); - const int bit_depth = png_get_bit_depth(png_ptr, info_ptr); + const int width = png_get_image_width(png_ptr, info_ptr); + const int height = png_get_image_height(png_ptr, info_ptr); + const int channels = png_get_channels(png_ptr, info_ptr); + const int bit_depth = png_get_bit_depth(png_ptr, info_ptr); - const halide_type_t im_type(halide_type_uint, bit_depth); - std::vector im_dimensions = {width, height}; - if (channels != 1) { - im_dimensions.push_back(channels); - } + const halide_type_t im_type(halide_type_uint, bit_depth); + std::vector im_dimensions = { width, height }; + if (channels != 1) { + im_dimensions.push_back(channels); + } - *im = ImageType(im_type, im_dimensions); + *im = ImageType(im_type, im_dimensions); - png_read_update_info(png_ptr, info_ptr); + png_read_update_info(png_ptr, info_ptr); - auto copy_to_image = bit_depth == 8 ? - Internal::read_big_endian_row : - Internal::read_big_endian_row; + auto copy_to_image = bit_depth == 8 ? Internal::read_big_endian_row : Internal::read_big_endian_row; - std::vector row(png_get_rowbytes(png_ptr, info_ptr)); - const int ymin = im->dim(1).min(); - const int ymax = im->dim(1).max(); - for (int y = ymin; y <= ymax; ++y) { - png_read_row(png_ptr, row.data(), nullptr); - copy_to_image(row.data(), y, im); - } + std::vector row(png_get_rowbytes(png_ptr, info_ptr)); + const int ymin = im->dim(1).min(); + const int ymax = im->dim(1).max(); + for (int y = ymin; y <= ymax; ++y) { + png_read_row(png_ptr, row.data(), nullptr); + copy_to_image(row.data(), y, im); + } - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - return true; -} + return true; + } -inline const std::set &query_png() { - static std::set info = { - {halide_type_t(halide_type_uint, 8), 2}, - {halide_type_t(halide_type_uint, 16), 2}, - {halide_type_t(halide_type_uint, 8), 3}, - {halide_type_t(halide_type_uint, 16), 3}}; - return info; -} + inline const std::set& query_png() + { + static std::set info = { + { halide_type_t(halide_type_uint, 8), 2 }, + { halide_type_t(halide_type_uint, 16), 2 }, + { halide_type_t(halide_type_uint, 8), 3 }, + { halide_type_t(halide_type_uint, 16), 3 } + }; + return info; + } -// "im" is not const-ref because copy_to_host() is not const. -template -bool save_png(ImageType &im, const std::string &filename) { - static_assert(!ImageType::has_static_halide_type, ""); + // "im" is not const-ref because copy_to_host() is not const. + template + bool save_png(ImageType& im, const std::string& filename) + { + static_assert(!ImageType::has_static_halide_type, ""); - if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { - return false; - } + if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { + return false; + } - const int width = im.width(); - const int height = im.height(); - const int channels = im.channels(); + const int width = im.width(); + const int height = im.height(); + const int channels = im.channels(); - if (!check(channels >= 1 && channels <= 4, - "Can't write PNG files that have other than 1, 2, 3, or 4 channels")) { - return false; - } + if (!check(channels >= 1 && channels <= 4, + "Can't write PNG files that have other than 1, 2, 3, or 4 channels")) { + return false; + } - const png_byte color_types[4] = { - PNG_COLOR_TYPE_GRAY, - PNG_COLOR_TYPE_GRAY_ALPHA, - PNG_COLOR_TYPE_RGB, - PNG_COLOR_TYPE_RGB_ALPHA}; - png_byte color_type = color_types[channels - 1]; - - // open file - Internal::FileOpener f(filename, "wb"); - if (!check(f.f != nullptr, "[write_png_file] File could not be opened for writing")) { - return false; - } + const png_byte color_types[4] = { + PNG_COLOR_TYPE_GRAY, + PNG_COLOR_TYPE_GRAY_ALPHA, + PNG_COLOR_TYPE_RGB, + PNG_COLOR_TYPE_RGB_ALPHA + }; + png_byte color_type = color_types[channels - 1]; + + // open file + Internal::FileOpener f(filename, "wb"); + if (!check(f.f != nullptr, "[write_png_file] File could not be opened for writing")) { + return false; + } - // initialize stuff - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (!check(png_ptr != nullptr, "[write_png_file] png_create_write_struct failed")) { - return false; - } + // initialize stuff + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!check(png_ptr != nullptr, "[write_png_file] png_create_write_struct failed")) { + return false; + } - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!check(info_ptr != nullptr, "[write_png_file] png_create_info_struct failed")) { - return false; - } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!check(info_ptr != nullptr, "[write_png_file] png_create_info_struct failed")) { + return false; + } - if (!check(!setjmp(png_jmpbuf(png_ptr)), "Error saving PNG")) { - return false; - } + if (!check(!setjmp(png_jmpbuf(png_ptr)), "Error saving PNG")) { + return false; + } - png_init_io(png_ptr, f.f); + png_init_io(png_ptr, f.f); - const halide_type_t im_type = im.type(); - const int bit_depth = im_type.bits; + const halide_type_t im_type = im.type(); + const int bit_depth = im_type.bits; - png_set_IHDR(png_ptr, info_ptr, width, height, - bit_depth, color_type, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_set_IHDR(png_ptr, info_ptr, width, height, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - png_write_info(png_ptr, info_ptr); + png_write_info(png_ptr, info_ptr); - auto copy_from_image = bit_depth == 8 ? - Internal::write_big_endian_row : - Internal::write_big_endian_row; + auto copy_from_image = bit_depth == 8 ? Internal::write_big_endian_row : Internal::write_big_endian_row; - std::vector row(png_get_rowbytes(png_ptr, info_ptr)); - const int ymin = im.dim(1).min(); - const int ymax = im.dim(1).max(); - for (int y = ymin; y <= ymax; ++y) { - copy_from_image(im, y, row.data()); - png_write_row(png_ptr, row.data()); - } - png_write_end(png_ptr, nullptr); - png_destroy_write_struct(&png_ptr, &info_ptr); + std::vector row(png_get_rowbytes(png_ptr, info_ptr)); + const int ymin = im.dim(1).min(); + const int ymax = im.dim(1).max(); + for (int y = ymin; y <= ymax; ++y) { + copy_from_image(im, y, row.data()); + png_write_row(png_ptr, row.data()); + } + png_write_end(png_ptr, nullptr); + png_destroy_write_struct(&png_ptr, &info_ptr); - return true; -} + return true; + } -#endif // not HALIDE_NO_PNG +#endif // not HALIDE_NO_PNG -template -bool read_pnm_header(Internal::FileOpener &f, const std::string &hdr_fmt, int *width, int *height, int *bit_depth) { - if (!check(f.f != nullptr, "File could not be opened for reading")) { - return false; - } + template + bool read_pnm_header(Internal::FileOpener& f, const std::string& hdr_fmt, int* width, int* height, int* bit_depth) + { + if (!check(f.f != nullptr, "File could not be opened for reading")) { + return false; + } - char header[256]; - if (!check(f.scan_line("%255s", header) == 1, "Could not read header")) { - return false; - } + char header[256]; + if (!check(f.scan_line("%255s", header) == 1, "Could not read header")) { + return false; + } - if (!check(to_lowercase(hdr_fmt) == to_lowercase(header), "Unexpected file header")) { - return false; - } + if (!check(to_lowercase(hdr_fmt) == to_lowercase(header), "Unexpected file header")) { + return false; + } - if (!check(f.scan_line("%d %d\n", width, height) == 2, "Could not read width and height")) { - return false; - } + if (!check(f.scan_line("%d %d\n", width, height) == 2, "Could not read width and height")) { + return false; + } - int maxval; - if (!check(f.scan_line("%d", &maxval) == 1, "Could not read max value")) { - return false; - } - if (maxval == 255) { - *bit_depth = 8; - } else if (maxval == 65535) { - *bit_depth = 16; - } else { - *bit_depth = 0; - return check(false, "Invalid bit depth"); - } + int maxval; + if (!check(f.scan_line("%d", &maxval) == 1, "Could not read max value")) { + return false; + } + if (maxval == 255) { + *bit_depth = 8; + } else if (maxval == 65535) { + *bit_depth = 16; + } else { + *bit_depth = 0; + return check(false, "Invalid bit depth"); + } - return true; -} + return true; + } -template -bool load_pnm(const std::string &filename, int channels, ImageType *im) { - static_assert(!ImageType::has_static_halide_type, ""); + template + bool load_pnm(const std::string& filename, int channels, ImageType* im) + { + static_assert(!ImageType::has_static_halide_type, ""); - const char *hdr_fmt = channels == 3 ? "P6" : "P5"; + const char* hdr_fmt = channels == 3 ? "P6" : "P5"; - Internal::FileOpener f(filename, "rb"); - int width, height, bit_depth; - if (!Internal::read_pnm_header(f, hdr_fmt, &width, &height, &bit_depth)) { - return false; - } + Internal::FileOpener f(filename, "rb"); + int width, height, bit_depth; + if (!Internal::read_pnm_header(f, hdr_fmt, &width, &height, &bit_depth)) { + return false; + } - const halide_type_t im_type(halide_type_uint, bit_depth); - std::vector im_dimensions = {width, height}; - if (channels > 1) { - im_dimensions.push_back(channels); - } - *im = ImageType(im_type, im_dimensions); + const halide_type_t im_type(halide_type_uint, bit_depth); + std::vector im_dimensions = { width, height }; + if (channels > 1) { + im_dimensions.push_back(channels); + } + *im = ImageType(im_type, im_dimensions); - auto copy_to_image = bit_depth == 8 ? - Internal::read_big_endian_row : - Internal::read_big_endian_row; + auto copy_to_image = bit_depth == 8 ? Internal::read_big_endian_row : Internal::read_big_endian_row; - std::vector row(width * channels * (bit_depth / 8)); - const int ymin = im->dim(1).min(); - const int ymax = im->dim(1).max(); - for (int y = ymin; y <= ymax; ++y) { - if (!check(f.read_vector(&row), "Could not read data")) { - return false; + std::vector row(width * channels * (bit_depth / 8)); + const int ymin = im->dim(1).min(); + const int ymax = im->dim(1).max(); + for (int y = ymin; y <= ymax; ++y) { + if (!check(f.read_vector(&row), "Could not read data")) { + return false; + } + copy_to_image(row.data(), y, im); + } + + return true; } - copy_to_image(row.data(), y, im); - } - return true; -} + template + bool save_pnm(ImageType& im, const int channels, const std::string& filename) + { + static_assert(!ImageType::has_static_halide_type, ""); -template -bool save_pnm(ImageType &im, const int channels, const std::string &filename) { - static_assert(!ImageType::has_static_halide_type, ""); + if (!check(im.channels() == channels, "Wrong number of channels")) { + return false; + } - if (!check(im.channels() == channels, "Wrong number of channels")) { - return false; - } + if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { + return false; + } - if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { - return false; - } + const halide_type_t im_type = im.type(); + const int width = im.width(); + const int height = im.height(); + const int bit_depth = im_type.bits; - const halide_type_t im_type = im.type(); - const int width = im.width(); - const int height = im.height(); - const int bit_depth = im_type.bits; + Internal::FileOpener f(filename, "wb"); + if (!check(f.f != nullptr, "File could not be opened for writing")) { + return false; + } + const char* hdr_fmt = channels == 3 ? "P6" : "P5"; + fprintf(f.f, "%s\n%d %d\n%d\n", hdr_fmt, width, height, (1 << bit_depth) - 1); + + auto copy_from_image = bit_depth == 8 ? Internal::write_big_endian_row : Internal::write_big_endian_row; + + std::vector row(width * channels * (bit_depth / 8)); + const int ymin = im.dim(1).min(); + const int ymax = im.dim(1).max(); + for (int y = ymin; y <= ymax; ++y) { + copy_from_image(im, y, row.data()); + if (!check(f.write_vector(row), "Could not write data")) { + return false; + } + } - Internal::FileOpener f(filename, "wb"); - if (!check(f.f != nullptr, "File could not be opened for writing")) { - return false; - } - const char *hdr_fmt = channels == 3 ? "P6" : "P5"; - fprintf(f.f, "%s\n%d %d\n%d\n", hdr_fmt, width, height, (1 << bit_depth) - 1); - - auto copy_from_image = bit_depth == 8 ? - Internal::write_big_endian_row : - Internal::write_big_endian_row; - - std::vector row(width * channels * (bit_depth / 8)); - const int ymin = im.dim(1).min(); - const int ymax = im.dim(1).max(); - for (int y = ymin; y <= ymax; ++y) { - copy_from_image(im, y, row.data()); - if (!check(f.write_vector(row), "Could not write data")) { - return false; + return true; } - } - return true; -} - -template -bool load_pgm(const std::string &filename, ImageType *im) { - return Internal::load_pnm(filename, 1, im); -} - -inline const std::set &query_pgm() { - static std::set info = { - {halide_type_t(halide_type_uint, 8), 2}, - {halide_type_t(halide_type_uint, 16), 2}}; - return info; -} - -// "im" is not const-ref because copy_to_host() is not const. -template -bool save_pgm(ImageType &im, const std::string &filename) { - return Internal::save_pnm(im, 1, filename); -} - -template -bool load_ppm(const std::string &filename, ImageType *im) { - return Internal::load_pnm(filename, 3, im); -} - -inline const std::set &query_ppm() { - static std::set info = { - {halide_type_t(halide_type_uint, 8), 3}, - {halide_type_t(halide_type_uint, 16), 3}}; - return info; -} - -// "im" is not const-ref because copy_to_host() is not const. -template -bool save_ppm(ImageType &im, const std::string &filename) { - return Internal::save_pnm(im, 3, filename); -} + template + bool load_pgm(const std::string& filename, ImageType* im) + { + return Internal::load_pnm(filename, 1, im); + } + + inline const std::set& query_pgm() + { + static std::set info = { + { halide_type_t(halide_type_uint, 8), 2 }, + { halide_type_t(halide_type_uint, 16), 2 } + }; + return info; + } + + // "im" is not const-ref because copy_to_host() is not const. + template + bool save_pgm(ImageType& im, const std::string& filename) + { + return Internal::save_pnm(im, 1, filename); + } + + template + bool load_ppm(const std::string& filename, ImageType* im) + { + return Internal::load_pnm(filename, 3, im); + } + + inline const std::set& query_ppm() + { + static std::set info = { + { halide_type_t(halide_type_uint, 8), 3 }, + { halide_type_t(halide_type_uint, 16), 3 } + }; + return info; + } + + // "im" is not const-ref because copy_to_host() is not const. + template + bool save_ppm(ImageType& im, const std::string& filename) + { + return Internal::save_pnm(im, 3, filename); + } #ifndef HALIDE_NO_JPEG -template -bool load_jpg(const std::string &filename, ImageType *im) { - static_assert(!ImageType::has_static_halide_type, ""); + template + bool load_jpg(const std::string& filename, ImageType* im) + { + static_assert(!ImageType::has_static_halide_type, ""); - Internal::FileOpener f(filename, "rb"); - if (!check(f.f != nullptr, "File could not be opened for reading")) { - return false; - } + Internal::FileOpener f(filename, "rb"); + if (!check(f.f != nullptr, "File could not be opened for reading")) { + return false; + } - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, f.f); - jpeg_read_header(&cinfo, TRUE); - jpeg_start_decompress(&cinfo); - - const int width = cinfo.output_width; - const int height = cinfo.output_height; - const int channels = cinfo.output_components; - - const halide_type_t im_type(halide_type_uint, 8); - std::vector im_dimensions = {width, height}; - if (channels > 1) { - im_dimensions.push_back(channels); - } - *im = ImageType(im_type, im_dimensions); + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, f.f); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + const int width = cinfo.output_width; + const int height = cinfo.output_height; + const int channels = cinfo.output_components; + + const halide_type_t im_type(halide_type_uint, 8); + std::vector im_dimensions = { width, height }; + if (channels > 1) { + im_dimensions.push_back(channels); + } + *im = ImageType(im_type, im_dimensions); - auto copy_to_image = Internal::read_big_endian_row; + auto copy_to_image = Internal::read_big_endian_row; - std::vector row(width * channels); - const int ymin = im->dim(1).min(); - const int ymax = im->dim(1).max(); - for (int y = ymin; y <= ymax; ++y) { - uint8_t *src = row.data(); - jpeg_read_scanlines(&cinfo, &src, 1); - copy_to_image(row.data(), y, im); - } + std::vector row(width * channels); + const int ymin = im->dim(1).min(); + const int ymax = im->dim(1).max(); + for (int y = ymin; y <= ymax; ++y) { + uint8_t* src = row.data(); + jpeg_read_scanlines(&cinfo, &src, 1); + copy_to_image(row.data(), y, im); + } - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); - return true; -} + return true; + } -inline const std::set &query_jpg() { - static std::set info = { - {halide_type_t(halide_type_uint, 8), 2}, - {halide_type_t(halide_type_uint, 8), 3}, - }; - return info; -} + inline const std::set& query_jpg() + { + static std::set info = { + { halide_type_t(halide_type_uint, 8), 2 }, + { halide_type_t(halide_type_uint, 8), 3 }, + }; + return info; + } -template -bool save_jpg(ImageType &im, const std::string &filename) { - static_assert(!ImageType::has_static_halide_type, ""); + template + bool save_jpg(ImageType& im, const std::string& filename) + { + static_assert(!ImageType::has_static_halide_type, ""); - if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { - return false; - } + if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { + return false; + } - const int width = im.width(); - const int height = im.height(); - const int channels = im.channels(); - if (!check(channels == 1 || channels == 3, "Wrong number of channels")) { - return false; - } + const int width = im.width(); + const int height = im.height(); + const int channels = im.channels(); + if (!check(channels == 1 || channels == 3, "Wrong number of channels")) { + return false; + } - Internal::FileOpener f(filename, "wb"); - if (!check(f.f != nullptr, "File could not be opened for writing")) { - return false; - } + Internal::FileOpener f(filename, "wb"); + if (!check(f.f != nullptr, "File could not be opened for writing")) { + return false; + } - // TODO: Make this an argument? - constexpr int quality = 99; - - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - jpeg_stdio_dest(&cinfo, f.f); - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = channels; - cinfo.in_color_space = (channels == 3) ? JCS_RGB : JCS_GRAYSCALE; - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - jpeg_start_compress(&cinfo, TRUE); - - auto copy_from_image = Internal::write_big_endian_row; - - std::vector row(width * channels); - const int ymin = im.dim(1).min(); - const int ymax = im.dim(1).max(); - for (int y = ymin; y <= ymax; ++y) { - uint8_t *dst = row.data(); - copy_from_image(im, y, dst); - jpeg_write_scanlines(&cinfo, &dst, 1); - } + // TODO: Make this an argument? + constexpr int quality = 99; + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, f.f); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = channels; + cinfo.in_color_space = (channels == 3) ? JCS_RGB : JCS_GRAYSCALE; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + jpeg_start_compress(&cinfo, TRUE); + + auto copy_from_image = Internal::write_big_endian_row; + + std::vector row(width * channels); + const int ymin = im.dim(1).min(); + const int ymax = im.dim(1).max(); + for (int y = ymin; y <= ymax; ++y) { + uint8_t* dst = row.data(); + copy_from_image(im, y, dst); + jpeg_write_scanlines(&cinfo, &dst, 1); + } - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - - return true; -} - -#endif // not HALIDE_NO_JPEG - -constexpr int kNumTmpCodes = 10; - -inline const halide_type_t *tmp_code_to_halide_type() { - static const halide_type_t tmp_code_to_halide_type_[kNumTmpCodes] = { - {halide_type_float, 32}, - {halide_type_float, 64}, - {halide_type_uint, 8}, - {halide_type_int, 8}, - {halide_type_uint, 16}, - {halide_type_int, 16}, - {halide_type_uint, 32}, - {halide_type_int, 32}, - {halide_type_uint, 64}, - {halide_type_int, 64}}; - return tmp_code_to_halide_type_; -} - -// return true iff the buffer storage has no padding between -// any elements, and is in strictly planar order. -template -bool buffer_is_compact_planar(ImageType &im) { - const halide_type_t im_type = im.type(); - const size_t elem_size = (im_type.bits / 8); - if (((const uint8_t *)im.begin() + (im.number_of_elements() * elem_size)) != (const uint8_t *)im.end()) { - return false; - } - for (int d = 1; d < im.dimensions(); ++d) { - if (im.dim(d - 1).stride() > im.dim(d).stride()) { - return false; + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + return true; } - // Strides can only match if the previous dimension has extent 1 - // (this can happen when artificially adding dimension(s), e.g. - // to write a .tmp file) - if (im.dim(d - 1).stride() == im.dim(d).stride() && im.dim(d - 1).extent() != 1) { - return false; + +#endif // not HALIDE_NO_JPEG + + constexpr int kNumTmpCodes = 10; + + inline const halide_type_t* tmp_code_to_halide_type() + { + static const halide_type_t tmp_code_to_halide_type_[kNumTmpCodes] = { + { halide_type_float, 32 }, + { halide_type_float, 64 }, + { halide_type_uint, 8 }, + { halide_type_int, 8 }, + { halide_type_uint, 16 }, + { halide_type_int, 16 }, + { halide_type_uint, 32 }, + { halide_type_int, 32 }, + { halide_type_uint, 64 }, + { halide_type_int, 64 } + }; + return tmp_code_to_halide_type_; } - } - return true; -} -// ".tmp" is a file format used by the ImageStack tool (see https://github.com/abadams/ImageStack) -template -bool load_tmp(const std::string &filename, ImageType *im) { - static_assert(!ImageType::has_static_halide_type, ""); + // return true iff the buffer storage has no padding between + // any elements, and is in strictly planar order. + template + bool buffer_is_compact_planar(ImageType& im) + { + const halide_type_t im_type = im.type(); + const size_t elem_size = (im_type.bits / 8); + if (((const uint8_t*)im.begin() + (im.number_of_elements() * elem_size)) != (const uint8_t*)im.end()) { + return false; + } + for (int d = 1; d < im.dimensions(); ++d) { + if (im.dim(d - 1).stride() > im.dim(d).stride()) { + return false; + } + // Strides can only match if the previous dimension has extent 1 + // (this can happen when artificially adding dimension(s), e.g. + // to write a .tmp file) + if (im.dim(d - 1).stride() == im.dim(d).stride() && im.dim(d - 1).extent() != 1) { + return false; + } + } + return true; + } - FileOpener f(filename, "rb"); - if (!check(f.f != nullptr, "File could not be opened for reading")) { - return false; - } + // ".tmp" is a file format used by the ImageStack tool (see https://github.com/abadams/ImageStack) + template + bool load_tmp(const std::string& filename, ImageType* im) + { + static_assert(!ImageType::has_static_halide_type, ""); - int32_t header[5]; - if (!check(f.read_array(header), "Count not read .tmp header")) { - return false; - } + FileOpener f(filename, "rb"); + if (!check(f.f != nullptr, "File could not be opened for reading")) { + return false; + } - if (!check(header[0] > 0 && header[1] > 0 && header[2] > 0 && header[3] > 0 && - header[4] >= 0 && header[4] < kNumTmpCodes, - "Bad header on .tmp file")) { - return false; - } + int32_t header[5]; + if (!check(f.read_array(header), "Count not read .tmp header")) { + return false; + } - const halide_type_t im_type = tmp_code_to_halide_type()[header[4]]; - std::vector im_dimensions = {header[0], header[1], header[2], header[3]}; - *im = ImageType(im_type, im_dimensions); + if (!check(header[0] > 0 && header[1] > 0 && header[2] > 0 && header[3] > 0 && header[4] >= 0 && header[4] < kNumTmpCodes, + "Bad header on .tmp file")) { + return false; + } - // This should never fail unless the default Buffer<> constructor behavior changes. - if (!check(buffer_is_compact_planar(*im), "load_tmp() requires compact planar images")) { - return false; - } + const halide_type_t im_type = tmp_code_to_halide_type()[header[4]]; + std::vector im_dimensions = { header[0], header[1], header[2], header[3] }; + *im = ImageType(im_type, im_dimensions); - if (!check(f.read_bytes(im->begin(), im->size_in_bytes()), "Count not read .tmp payload")) { - return false; - } + // This should never fail unless the default Buffer<> constructor behavior changes. + if (!check(buffer_is_compact_planar(*im), "load_tmp() requires compact planar images")) { + return false; + } - im->set_host_dirty(); - return true; -} - -inline const std::set &query_tmp() { - // TMP files require exactly 4 dimensions. - static std::set info = { - {halide_type_t(halide_type_float, 32), 4}, - {halide_type_t(halide_type_float, 64), 4}, - {halide_type_t(halide_type_uint, 8), 4}, - {halide_type_t(halide_type_int, 8), 4}, - {halide_type_t(halide_type_uint, 16), 4}, - {halide_type_t(halide_type_int, 16), 4}, - {halide_type_t(halide_type_uint, 32), 4}, - {halide_type_t(halide_type_int, 32), 4}, - {halide_type_t(halide_type_uint, 64), 4}, - {halide_type_t(halide_type_int, 64), 4}, - }; - return info; -} - -template -bool write_planar_payload(ImageType &im, FileOpener &f) { - if (im.dimensions() == 0 || buffer_is_compact_planar(im)) { - // Contiguous buffer! Write it all in one swell foop. - if (!check(f.write_bytes(im.begin(), im.size_in_bytes()), "Count not write .tmp payload")) { - return false; - } - } else { - // We have to do this the hard way. - int d = im.dimensions() - 1; - for (int i = im.dim(d).min(); i <= im.dim(d).max(); i++) { - auto slice = im.sliced(d, i); - if (!write_planar_payload(slice, f)) { + if (!check(f.read_bytes(im->begin(), im->size_in_bytes()), "Count not read .tmp payload")) { return false; } + + im->set_host_dirty(); + return true; } - } - return true; -} -// ".tmp" is a file format used by the ImageStack tool (see https://github.com/abadams/ImageStack) -template -bool save_tmp(ImageType &im, const std::string &filename) { - static_assert(!ImageType::has_static_halide_type, ""); + inline const std::set& query_tmp() + { + // TMP files require exactly 4 dimensions. + static std::set info = { + { halide_type_t(halide_type_float, 32), 4 }, + { halide_type_t(halide_type_float, 64), 4 }, + { halide_type_t(halide_type_uint, 8), 4 }, + { halide_type_t(halide_type_int, 8), 4 }, + { halide_type_t(halide_type_uint, 16), 4 }, + { halide_type_t(halide_type_int, 16), 4 }, + { halide_type_t(halide_type_uint, 32), 4 }, + { halide_type_t(halide_type_int, 32), 4 }, + { halide_type_t(halide_type_uint, 64), 4 }, + { halide_type_t(halide_type_int, 64), 4 }, + }; + return info; + } - if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { - return false; - } + template + bool write_planar_payload(ImageType& im, FileOpener& f) + { + if (im.dimensions() == 0 || buffer_is_compact_planar(im)) { + // Contiguous buffer! Write it all in one swell foop. + if (!check(f.write_bytes(im.begin(), im.size_in_bytes()), "Count not write .tmp payload")) { + return false; + } + } else { + // We have to do this the hard way. + int d = im.dimensions() - 1; + for (int i = im.dim(d).min(); i <= im.dim(d).max(); i++) { + auto slice = im.sliced(d, i); + if (!write_planar_payload(slice, f)) { + return false; + } + } + } + return true; + } - int32_t header[5] = {1, 1, 1, 1, -1}; - for (int i = 0; i < im.dimensions(); ++i) { - header[i] = im.dim(i).extent(); - } - const auto *table = tmp_code_to_halide_type(); - for (int i = 0; i < kNumTmpCodes; i++) { - if (im.type() == table[i]) { - header[4] = i; - break; + // ".tmp" is a file format used by the ImageStack tool (see https://github.com/abadams/ImageStack) + template + bool save_tmp(ImageType& im, const std::string& filename) + { + static_assert(!ImageType::has_static_halide_type, ""); + + if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { + return false; + } + + int32_t header[5] = { 1, 1, 1, 1, -1 }; + for (int i = 0; i < im.dimensions(); ++i) { + header[i] = im.dim(i).extent(); + } + const auto* table = tmp_code_to_halide_type(); + for (int i = 0; i < kNumTmpCodes; i++) { + if (im.type() == table[i]) { + header[4] = i; + break; + } + } + if (!check(header[4] >= 0, "Unsupported type for .tmp file")) { + return false; + } + + FileOpener f(filename, "wb"); + if (!check(f.f != nullptr, "File could not be opened for writing")) { + return false; + } + if (!check(f.write_array(header), "Could not write .tmp header")) { + return false; + } + + if (!write_planar_payload(im, f)) { + return false; + } + + return true; } - } - if (!check(header[4] >= 0, "Unsupported type for .tmp file")) { - return false; - } - FileOpener f(filename, "wb"); - if (!check(f.f != nullptr, "File could not be opened for writing")) { - return false; - } - if (!check(f.write_array(header), "Could not write .tmp header")) { - return false; - } + // ".mat" is the matlab level 5 format documented here: + // http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf + + enum MatlabTypeCode { + miINT8 = 1, + miUINT8 = 2, + miINT16 = 3, + miUINT16 = 4, + miINT32 = 5, + miUINT32 = 6, + miSINGLE = 7, + miDOUBLE = 9, + miINT64 = 12, + miUINT64 = 13, + miMATRIX = 14, + miCOMPRESSED = 15, + miUTF8 = 16, + miUTF16 = 17, + miUTF32 = 18 + }; + + enum MatlabClassCode { + mxCHAR_CLASS = 3, + mxDOUBLE_CLASS = 6, + mxSINGLE_CLASS = 7, + mxINT8_CLASS = 8, + mxUINT8_CLASS = 9, + mxINT16_CLASS = 10, + mxUINT16_CLASS = 11, + mxINT32_CLASS = 12, + mxUINT32_CLASS = 13, + mxINT64_CLASS = 14, + mxUINT64_CLASS = 15 + }; + + template + bool load_mat(const std::string& filename, ImageType* im) + { + static_assert(!ImageType::has_static_halide_type, ""); + + FileOpener f(filename, "rb"); + if (!check(f.f != nullptr, "File could not be opened for reading")) { + return false; + } + + uint8_t header[128]; + if (!check(f.read_array(header), "Could not read .mat header\n")) { + return false; + } + + // Matrix header + uint32_t matrix_header[2]; + if (!check(f.read_array(matrix_header), "Could not read .mat header\n")) { + return false; + } + if (!check(matrix_header[0] == miMATRIX, "Could not parse this .mat file: bad matrix header\n")) { + return false; + } + + // Array flags + uint32_t flags[4]; + if (!check(f.read_array(flags), "Could not read .mat header\n")) { + return false; + } + if (!check(flags[0] == miUINT32 && flags[1] == 8, "Could not parse this .mat file: bad flags\n")) { + return false; + } - if (!write_planar_payload(im, f)) { - return false; - } + // Shape + uint32_t shape_header[2]; + if (!check(f.read_array(shape_header), "Could not read .mat header\n")) { + return false; + } + if (!check(shape_header[0] == miINT32, "Could not parse this .mat file: bad shape header\n")) { + return false; + } + int dims = shape_header[1] / 4; + std::vector extents(dims); + if (!check(f.read_vector(&extents), "Could not read .mat header\n")) { + return false; + } + if (dims & 1) { + uint32_t padding; + if (!check(f.read_bytes(&padding, 4), "Could not read .mat header\n")) { + return false; + } + } - return true; -} - -// ".mat" is the matlab level 5 format documented here: -// http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf - -enum MatlabTypeCode { - miINT8 = 1, - miUINT8 = 2, - miINT16 = 3, - miUINT16 = 4, - miINT32 = 5, - miUINT32 = 6, - miSINGLE = 7, - miDOUBLE = 9, - miINT64 = 12, - miUINT64 = 13, - miMATRIX = 14, - miCOMPRESSED = 15, - miUTF8 = 16, - miUTF16 = 17, - miUTF32 = 18 -}; - -enum MatlabClassCode { - mxCHAR_CLASS = 3, - mxDOUBLE_CLASS = 6, - mxSINGLE_CLASS = 7, - mxINT8_CLASS = 8, - mxUINT8_CLASS = 9, - mxINT16_CLASS = 10, - mxUINT16_CLASS = 11, - mxINT32_CLASS = 12, - mxUINT32_CLASS = 13, - mxINT64_CLASS = 14, - mxUINT64_CLASS = 15 -}; - -template -bool load_mat(const std::string &filename, ImageType *im) { - static_assert(!ImageType::has_static_halide_type, ""); - - FileOpener f(filename, "rb"); - if (!check(f.f != nullptr, "File could not be opened for reading")) { - return false; - } + // Skip over the name + uint32_t name_header[2]; + if (!check(f.read_array(name_header), "Could not read .mat header\n")) { + return false; + } - uint8_t header[128]; - if (!check(f.read_array(header), "Could not read .mat header\n")) { - return false; - } + if (name_header[0] >> 16) { + // Name must be fewer than 4 chars, and so the whole name + // field was stored packed into 8 bytes + } else { + if (!check(name_header[0] == miINT8, "Could not parse this .mat file: bad name header\n")) { + return false; + } + std::vector scratch((name_header[1] + 7) / 8); + if (!check(f.read_vector(&scratch), "Could not read .mat header\n")) { + return false; + } + } - // Matrix header - uint32_t matrix_header[2]; - if (!check(f.read_array(matrix_header), "Could not read .mat header\n")) { - return false; - } - if (!check(matrix_header[0] == miMATRIX, "Could not parse this .mat file: bad matrix header\n")) { - return false; - } + // Payload header + uint32_t payload_header[2]; + if (!check(f.read_array(payload_header), "Could not read .mat header\n")) { + return false; + } + halide_type_t type; + switch (payload_header[0]) { + case miINT8: + type = halide_type_of(); + break; + case miINT16: + type = halide_type_of(); + break; + case miINT32: + type = halide_type_of(); + break; + case miINT64: + type = halide_type_of(); + break; + case miUINT8: + type = halide_type_of(); + break; + case miUINT16: + type = halide_type_of(); + break; + case miUINT32: + type = halide_type_of(); + break; + case miUINT64: + type = halide_type_of(); + break; + case miSINGLE: + type = halide_type_of(); + break; + case miDOUBLE: + type = halide_type_of(); + break; + } - // Array flags - uint32_t flags[4]; - if (!check(f.read_array(flags), "Could not read .mat header\n")) { - return false; - } - if (!check(flags[0] == miUINT32 && flags[1] == 8, "Could not parse this .mat file: bad flags\n")) { - return false; - } + *im = ImageType(type, extents); - // Shape - uint32_t shape_header[2]; - if (!check(f.read_array(shape_header), "Could not read .mat header\n")) { - return false; - } - if (!check(shape_header[0] == miINT32, "Could not parse this .mat file: bad shape header\n")) { - return false; - } - int dims = shape_header[1] / 4; - std::vector extents(dims); - if (!check(f.read_vector(&extents), "Could not read .mat header\n")) { - return false; - } - if (dims & 1) { - uint32_t padding; - if (!check(f.read_bytes(&padding, 4), "Could not read .mat header\n")) { - return false; - } - } + // This should never fail unless the default Buffer<> constructor behavior changes. + if (!check(buffer_is_compact_planar(*im), "load_mat() requires compact planar images")) { + return false; + } - // Skip over the name - uint32_t name_header[2]; - if (!check(f.read_array(name_header), "Could not read .mat header\n")) { - return false; - } + if (!check(f.read_bytes(im->begin(), im->size_in_bytes()), "Could not read .tmp payload")) { + return false; + } - if (name_header[0] >> 16) { - // Name must be fewer than 4 chars, and so the whole name - // field was stored packed into 8 bytes - } else { - if (!check(name_header[0] == miINT8, "Could not parse this .mat file: bad name header\n")) { - return false; - } - std::vector scratch((name_header[1] + 7) / 8); - if (!check(f.read_vector(&scratch), "Could not read .mat header\n")) { - return false; + im->set_host_dirty(); + return true; } - } - - // Payload header - uint32_t payload_header[2]; - if (!check(f.read_array(payload_header), "Could not read .mat header\n")) { - return false; - } - halide_type_t type; - switch (payload_header[0]) { - case miINT8: - type = halide_type_of(); - break; - case miINT16: - type = halide_type_of(); - break; - case miINT32: - type = halide_type_of(); - break; - case miINT64: - type = halide_type_of(); - break; - case miUINT8: - type = halide_type_of(); - break; - case miUINT16: - type = halide_type_of(); - break; - case miUINT32: - type = halide_type_of(); - break; - case miUINT64: - type = halide_type_of(); - break; - case miSINGLE: - type = halide_type_of(); - break; - case miDOUBLE: - type = halide_type_of(); - break; - } - *im = ImageType(type, extents); + inline const std::set& query_mat() + { + // MAT files must have at least 2 dimensions, but there's no upper + // bound. Our support arbitrarily stops at 16 dimensions. + static std::set info = []() { + std::set s; + for (int i = 2; i < 16; i++) { + s.insert({ halide_type_t(halide_type_float, 32), i }); + s.insert({ halide_type_t(halide_type_float, 64), i }); + s.insert({ halide_type_t(halide_type_uint, 8), i }); + s.insert({ halide_type_t(halide_type_int, 8), i }); + s.insert({ halide_type_t(halide_type_uint, 16), i }); + s.insert({ halide_type_t(halide_type_int, 16), i }); + s.insert({ halide_type_t(halide_type_uint, 32), i }); + s.insert({ halide_type_t(halide_type_int, 32), i }); + s.insert({ halide_type_t(halide_type_uint, 64), i }); + s.insert({ halide_type_t(halide_type_int, 64), i }); + } + return s; + }(); + return info; + } - // This should never fail unless the default Buffer<> constructor behavior changes. - if (!check(buffer_is_compact_planar(*im), "load_mat() requires compact planar images")) { - return false; - } + template + bool save_mat(ImageType& im, const std::string& filename) + { + static_assert(!ImageType::has_static_halide_type, ""); - if (!check(f.read_bytes(im->begin(), im->size_in_bytes()), "Could not read .tmp payload")) { - return false; - } + if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { + return false; + } - im->set_host_dirty(); - return true; -} - -inline const std::set &query_mat() { - // MAT files must have at least 2 dimensions, but there's no upper - // bound. Our support arbitrarily stops at 16 dimensions. - static std::set info = []() { - std::set s; - for (int i = 2; i < 16; i++) { - s.insert({halide_type_t(halide_type_float, 32), i}); - s.insert({halide_type_t(halide_type_float, 64), i}); - s.insert({halide_type_t(halide_type_uint, 8), i}); - s.insert({halide_type_t(halide_type_int, 8), i}); - s.insert({halide_type_t(halide_type_uint, 16), i}); - s.insert({halide_type_t(halide_type_int, 16), i}); - s.insert({halide_type_t(halide_type_uint, 32), i}); - s.insert({halide_type_t(halide_type_int, 32), i}); - s.insert({halide_type_t(halide_type_uint, 64), i}); - s.insert({halide_type_t(halide_type_int, 64), i}); - } - return s; - }(); - return info; -} - -template -bool save_mat(ImageType &im, const std::string &filename) { - static_assert(!ImageType::has_static_halide_type, ""); - - if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { - return false; - } + uint32_t class_code = 0, type_code = 0; + switch (im.raw_buffer()->type.code) { + case halide_type_int: + switch (im.raw_buffer()->type.bits) { + case 8: + class_code = mxINT8_CLASS; + type_code = miINT8; + break; + case 16: + class_code = mxINT16_CLASS; + type_code = miINT16; + break; + case 32: + class_code = mxINT32_CLASS; + type_code = miINT32; + break; + case 64: + class_code = mxINT64_CLASS; + type_code = miINT64; + break; + default: + check(false, "unreachable"); + }; + break; + case halide_type_uint: + switch (im.raw_buffer()->type.bits) { + case 8: + class_code = mxUINT8_CLASS; + type_code = miUINT8; + break; + case 16: + class_code = mxUINT16_CLASS; + type_code = miUINT16; + break; + case 32: + class_code = mxUINT32_CLASS; + type_code = miUINT32; + break; + case 64: + class_code = mxUINT64_CLASS; + type_code = miUINT64; + break; + default: + check(false, "unreachable"); + }; + break; + case halide_type_float: + switch (im.raw_buffer()->type.bits) { + case 16: + check(false, "float16 not supported by .mat"); + break; + case 32: + class_code = mxSINGLE_CLASS; + type_code = miSINGLE; + break; + case 64: + class_code = mxDOUBLE_CLASS; + type_code = miDOUBLE; + break; + default: + check(false, "unreachable"); + }; + break; + case halide_type_bfloat: + check(false, "bfloat not supported by .mat"); + break; + default: + check(false, "unreachable"); + } - uint32_t class_code = 0, type_code = 0; - switch (im.raw_buffer()->type.code) { - case halide_type_int: - switch (im.raw_buffer()->type.bits) { - case 8: - class_code = mxINT8_CLASS; - type_code = miINT8; - break; - case 16: - class_code = mxINT16_CLASS; - type_code = miINT16; - break; - case 32: - class_code = mxINT32_CLASS; - type_code = miINT32; - break; - case 64: - class_code = mxINT64_CLASS; - type_code = miINT64; - break; - default: - check(false, "unreachable"); - }; - break; - case halide_type_uint: - switch (im.raw_buffer()->type.bits) { - case 8: - class_code = mxUINT8_CLASS; - type_code = miUINT8; - break; - case 16: - class_code = mxUINT16_CLASS; - type_code = miUINT16; - break; - case 32: - class_code = mxUINT32_CLASS; - type_code = miUINT32; - break; - case 64: - class_code = mxUINT64_CLASS; - type_code = miUINT64; - break; - default: - check(false, "unreachable"); - }; - break; - case halide_type_float: - switch (im.raw_buffer()->type.bits) { - case 16: - check(false, "float16 not supported by .mat"); - break; - case 32: - class_code = mxSINGLE_CLASS; - type_code = miSINGLE; - break; - case 64: - class_code = mxDOUBLE_CLASS; - type_code = miDOUBLE; - break; - default: - check(false, "unreachable"); - }; - break; - case halide_type_bfloat: - check(false, "bfloat not supported by .mat"); - break; - default: - check(false, "unreachable"); - } + FileOpener f(filename, "wb"); + if (!check(f.f != nullptr, "File could not be opened for writing")) { + return false; + } - FileOpener f(filename, "wb"); - if (!check(f.f != nullptr, "File could not be opened for writing")) { - return false; - } + // Pick a name for the array + size_t idx = filename.rfind('.'); + std::string name = filename.substr(0, idx); + idx = filename.rfind('/'); + if (idx != std::string::npos) { + name = name.substr(idx + 1); + } - // Pick a name for the array - size_t idx = filename.rfind('.'); - std::string name = filename.substr(0, idx); - idx = filename.rfind('/'); - if (idx != std::string::npos) { - name = name.substr(idx + 1); - } + // Matlab variable names conform to similar rules as C + if (name.empty() || !std::isalpha(name[0])) { + name = "v" + name; + } + for (char& c : name) { + if (!std::isalnum(c)) { + c = '_'; + } + } - // Matlab variable names conform to similar rules as C - if (name.empty() || !std::isalpha(name[0])) { - name = "v" + name; - } - for (char &c : name) { - if (!std::isalnum(c)) { - c = '_'; - } - } + uint32_t name_size = (int)name.size(); + while (name.size() & 0x7) { + name += '\0'; + } - uint32_t name_size = (int)name.size(); - while (name.size() & 0x7) { - name += '\0'; - } + char header[128] = "MATLAB 5.0 MAT-file, produced by Halide"; + int len = strlen(header); + memset(header + len, ' ', sizeof(header) - len); - char header[128] = "MATLAB 5.0 MAT-file, produced by Halide"; - int len = strlen(header); - memset(header + len, ' ', sizeof(header) - len); + // Version + *((uint16_t*)(header + 124)) = 0x0100; - // Version - *((uint16_t *)(header + 124)) = 0x0100; + // Endianness check + header[126] = 'I'; + header[127] = 'M'; - // Endianness check - header[126] = 'I'; - header[127] = 'M'; + uint64_t payload_bytes = im.size_in_bytes(); - uint64_t payload_bytes = im.size_in_bytes(); + if (!check((payload_bytes >> 32) == 0, "Buffer too large to save as .mat")) { + return false; + } - if (!check((payload_bytes >> 32) == 0, "Buffer too large to save as .mat")) { - return false; - } + int dims = im.dimensions(); + if (dims < 2) { + dims = 2; + } + int padded_dims = dims + (dims & 1); + + uint32_t padding_bytes = 7 - ((payload_bytes - 1) & 7); + + // Matrix header + uint32_t matrix_header[2] = { + miMATRIX, 40 + padded_dims * 4 + (uint32_t)name.size() + (uint32_t)payload_bytes + padding_bytes + }; + + // Array flags + uint32_t flags[4] = { + miUINT32, 8, class_code, 1 + }; + + // Shape + int32_t shape[2] = { + miINT32, + im.dimensions() * 4, + }; + std::vector extents(im.dimensions()); + for (int d = 0; d < im.dimensions(); d++) { + extents[d] = im.dim(d).extent(); + } + while ((int)extents.size() < dims) { + extents.push_back(1); + } + while ((int)extents.size() < padded_dims) { + extents.push_back(0); + } - int dims = im.dimensions(); - if (dims < 2) { - dims = 2; - } - int padded_dims = dims + (dims & 1); + // Name + uint32_t name_header[2] = { + miINT8, name_size + }; - uint32_t padding_bytes = 7 - ((payload_bytes - 1) & 7); + // Payload header + uint32_t payload_header[2] = { + type_code, (uint32_t)payload_bytes + }; - // Matrix header - uint32_t matrix_header[2] = { - miMATRIX, 40 + padded_dims * 4 + (uint32_t)name.size() + (uint32_t)payload_bytes + padding_bytes}; + bool success = f.write_array(header) && f.write_array(matrix_header) && f.write_array(flags) && f.write_array(shape) && f.write_vector(extents) && f.write_array(name_header) && f.write_bytes(&name[0], name.size()) && f.write_array(payload_header); - // Array flags - uint32_t flags[4] = { - miUINT32, 8, class_code, 1}; + if (!check(success, "Could not write .mat header")) { + return false; + } - // Shape - int32_t shape[2] = { - miINT32, - im.dimensions() * 4, - }; - std::vector extents(im.dimensions()); - for (int d = 0; d < im.dimensions(); d++) { - extents[d] = im.dim(d).extent(); - } - while ((int)extents.size() < dims) { - extents.push_back(1); - } - while ((int)extents.size() < padded_dims) { - extents.push_back(0); - } + if (!write_planar_payload(im, f)) { + return false; + } - // Name - uint32_t name_header[2] = { - miINT8, name_size}; - - // Payload header - uint32_t payload_header[2] = { - type_code, (uint32_t)payload_bytes}; - - bool success = - f.write_array(header) && - f.write_array(matrix_header) && - f.write_array(flags) && - f.write_array(shape) && - f.write_vector(extents) && - f.write_array(name_header) && - f.write_bytes(&name[0], name.size()) && - f.write_array(payload_header); - - if (!check(success, "Could not write .mat header")) { - return false; - } + // Padding + if (!check(padding_bytes < 8, "Too much padding!\n")) { + return false; + } + uint64_t padding = 0; + if (!f.write_bytes(&padding, padding_bytes)) { + return false; + } - if (!write_planar_payload(im, f)) { - return false; - } + return true; + } - // Padding - if (!check(padding_bytes < 8, "Too much padding!\n")) { - return false; - } - uint64_t padding = 0; - if (!f.write_bytes(&padding, padding_bytes)) { - return false; - } + template + bool load_tiff(const std::string& filename, ImageType* im) + { + static_assert(!ImageType::has_static_halide_type, ""); + check(false, "Reading TIFF is not yet supported"); + return false; + } - return true; -} - -template -bool load_tiff(const std::string &filename, ImageType *im) { - static_assert(!ImageType::has_static_halide_type, ""); - check(false, "Reading TIFF is not yet supported"); - return false; -} - -inline const std::set &query_tiff() { - auto build_set = []() -> std::set { - std::set s; - for (halide_type_code_t code : {halide_type_int, halide_type_uint, halide_type_float}) { - for (int bits : {8, 16, 32, 64}) { - for (int dims : {1, 2, 3, 4}) { - if (code == halide_type_float && bits < 32) { - continue; + inline const std::set& query_tiff() + { + auto build_set = []() -> std::set { + std::set s; + for (halide_type_code_t code : { halide_type_int, halide_type_uint, halide_type_float }) { + for (int bits : { 8, 16, 32, 64 }) { + for (int dims : { 1, 2, 3, 4 }) { + if (code == halide_type_float && bits < 32) { + continue; + } + s.insert({ halide_type_t(code, bits), dims }); + } } - s.insert({halide_type_t(code, bits), dims}); } - } - } - return s; - }; + return s; + }; - static std::set info = build_set(); - return info; -} + static std::set info = build_set(); + return info; + } #pragma pack(push) #pragma pack(2) -struct halide_tiff_tag { - uint16_t tag_code; - int16_t type_code; - int32_t count; - union { - int8_t i8; - int16_t i16; - int32_t i32; - } value; - - void assign16(uint16_t tag_code, int32_t count, int16_t value) { - this->tag_code = tag_code; - this->type_code = 3; // SHORT - this->count = count; - this->value.i16 = value; - } + struct halide_tiff_tag { + uint16_t tag_code; + int16_t type_code; + int32_t count; + union { + int8_t i8; + int16_t i16; + int32_t i32; + } value; + + void assign16(uint16_t tag_code, int32_t count, int16_t value) + { + this->tag_code = tag_code; + this->type_code = 3; // SHORT + this->count = count; + this->value.i16 = value; + } - void assign32(uint16_t tag_code, int32_t count, int32_t value) { - this->tag_code = tag_code; - this->type_code = 4; // LONG - this->count = count; - this->value.i32 = value; - } + void assign32(uint16_t tag_code, int32_t count, int32_t value) + { + this->tag_code = tag_code; + this->type_code = 4; // LONG + this->count = count; + this->value.i32 = value; + } - void assign32(uint16_t tag_code, int16_t type_code, int32_t count, int32_t value) { - this->tag_code = tag_code; - this->type_code = type_code; - this->count = count; - this->value.i32 = value; - } -}; - -struct halide_tiff_header { - int16_t byte_order_marker; - int16_t version; - int32_t ifd0_offset; - int16_t entry_count; - halide_tiff_tag entries[15]; - int32_t ifd0_end; - int32_t width_resolution[2]; - int32_t height_resolution[2]; -}; + void assign32(uint16_t tag_code, int16_t type_code, int32_t count, int32_t value) + { + this->tag_code = tag_code; + this->type_code = type_code; + this->count = count; + this->value.i32 = value; + } + }; + + struct halide_tiff_header { + int16_t byte_order_marker; + int16_t version; + int32_t ifd0_offset; + int16_t entry_count; + halide_tiff_tag entries[15]; + int32_t ifd0_end; + int32_t width_resolution[2]; + int32_t height_resolution[2]; + }; #pragma pack(pop) -template -struct ElemWriter { - ElemWriter(FileOpener *f) - : f(f), next(&buf[0]) { - } - ~ElemWriter() { - flush(); - } + template + struct ElemWriter { + ElemWriter(FileOpener* f) + : f(f) + , next(&buf[0]) + { + } + ~ElemWriter() + { + flush(); + } - void operator()(const ElemType &elem) { - if (!ok) { - return; - } + void operator()(const ElemType& elem) + { + if (!ok) { + return; + } - *next++ = elem; - if (next == &buf[BUFFER_SIZE]) { - flush(); - } - } + *next++ = elem; + if (next == &buf[BUFFER_SIZE]) { + flush(); + } + } - void flush() { - if (!ok) { - return; - } + void flush() + { + if (!ok) { + return; + } - if (next > buf) { - if (!f->write_bytes(buf, (next - buf) * sizeof(ElemType))) { - ok = false; + if (next > buf) { + if (!f->write_bytes(buf, (next - buf) * sizeof(ElemType))) { + ok = false; + } + next = buf; + } } - next = buf; - } - } - FileOpener *const f; - ElemType buf[BUFFER_SIZE]; - ElemType *next; - bool ok = true; -}; - -// Note that this is a fairly simpleminded TIFF writer that doesn't -// do any compression. It would be desirable to (optionally) support using libtiff -// here instead, which would also allow us to provide a useful implementation -// for TIFF reading. -template -bool save_tiff(ImageType &im, const std::string &filename) { - static_assert(!ImageType::has_static_halide_type, ""); - - if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { - return false; - } + FileOpener* const f; + ElemType buf[BUFFER_SIZE]; + ElemType* next; + bool ok = true; + }; - if (!check(im.dimensions() <= 4, "Can only save TIFF files with <= 4 dimensions")) { - return false; - } + // Note that this is a fairly simpleminded TIFF writer that doesn't + // do any compression. It would be desirable to (optionally) support using libtiff + // here instead, which would also allow us to provide a useful implementation + // for TIFF reading. + template + bool save_tiff(ImageType& im, const std::string& filename) + { + static_assert(!ImageType::has_static_halide_type, ""); - FileOpener f(filename, "wb"); - if (!check(f.f != nullptr, "File could not be opened for writing")) { - return false; - } + if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { + return false; + } - const size_t elements = im.number_of_elements(); - halide_dimension_t shape[4]; - for (int i = 0; i < im.dimensions() && i < 4; i++) { - const auto &d = im.dim(i); - shape[i].min = d.min(); - shape[i].extent = d.extent(); - shape[i].stride = d.stride(); - } - for (int i = im.dimensions(); i < 4; i++) { - shape[i].min = 0; - shape[i].extent = 1; - shape[i].stride = 0; - } - const halide_type_t im_type = im.type(); - if (!check(im_type.code >= 0 && im_type.code < 3, "Unsupported image type")) { - return false; - } - const int32_t bytes_per_element = im_type.bytes(); - const int32_t width = shape[0].extent; - const int32_t height = shape[1].extent; - int32_t depth = shape[2].extent; - int32_t channels = shape[3].extent; - - if ((channels == 0 || channels == 1) && (depth < 5)) { - channels = depth; - depth = 1; - } + if (!check(im.dimensions() <= 4, "Can only save TIFF files with <= 4 dimensions")) { + return false; + } - // TIFF sample type values are: - // 0 => Signed int - // 1 => Unsigned int - // 2 => Floating-point - static const int16_t type_code_to_tiff_sample_type[] = { - 2, 1, 3}; - - struct halide_tiff_header header; - memset(&header, 0, sizeof(header)); - - const int32_t MMII = 0x4d4d4949; - // Select the appropriate two bytes signaling byte order automatically - const char *c = (const char *)&MMII; - header.byte_order_marker = (c[0] << 8) | c[1]; - header.version = 42; - header.ifd0_offset = offsetof(halide_tiff_header, entry_count); - header.entry_count = sizeof(header.entries) / sizeof(header.entries[0]); - - static_assert(sizeof(halide_tiff_tag) == 12, "Unexpected halide_tiff_tag packing"); - halide_tiff_tag *tag = &header.entries[0]; - tag++->assign32(256, 1, width); // ImageWidth - tag++->assign32(257, 1, height); // ImageLength - tag++->assign16(258, 1, int16_t(bytes_per_element * 8)); // BitsPerSample - tag++->assign16(259, 1, 1); // Compression -- none - tag++->assign16(262, 1, channels >= 3 ? 2 : 1); // PhotometricInterpretation -- black is zero or RGB - tag++->assign32(273, channels, sizeof(header)); // StripOffsets - tag++->assign16(277, 1, int16_t(channels)); // SamplesPerPixel - tag++->assign32(278, 1, height); // RowsPerStrip - tag++->assign32(279, channels, // StripByteCounts - (channels == 1) ? - elements * bytes_per_element : - sizeof(header) + channels * sizeof(int32_t)); // for channels > 1, this is an offset - tag++->assign32(282, 5, 1, - offsetof(halide_tiff_header, width_resolution)); // XResolution - tag++->assign32(283, 5, 1, - offsetof(halide_tiff_header, height_resolution)); // YResolution - tag++->assign16(284, 1, channels == 1 ? 1 : 2); // PlanarConfiguration -- contig or planar - tag++->assign16(296, 1, 1); // ResolutionUnit -- none - tag++->assign16(339, 1, type_code_to_tiff_sample_type[im_type.code]); // SampleFormat - tag++->assign32(32997, 1, depth); // Image depth - - // Verify we used exactly the number we declared - assert(tag == &header.entries[header.entry_count]); - - header.ifd0_end = 0; - header.width_resolution[0] = 1; - header.width_resolution[1] = 1; - header.height_resolution[0] = 1; - header.height_resolution[1] = 1; - - if (!check(f.write_bytes(&header, sizeof(header)), "TIFF write failed")) { - return false; - } + FileOpener f(filename, "wb"); + if (!check(f.f != nullptr, "File could not be opened for writing")) { + return false; + } - if (channels > 1) { - // Fill in the values for StripOffsets - int32_t offset = sizeof(header) + channels * sizeof(int32_t) * 2; - for (int32_t i = 0; i < channels; i++) { - if (!check(f.write_bytes(&offset, sizeof(offset)), "TIFF write failed")) { + const size_t elements = im.number_of_elements(); + halide_dimension_t shape[4]; + for (int i = 0; i < im.dimensions() && i < 4; i++) { + const auto& d = im.dim(i); + shape[i].min = d.min(); + shape[i].extent = d.extent(); + shape[i].stride = d.stride(); + } + for (int i = im.dimensions(); i < 4; i++) { + shape[i].min = 0; + shape[i].extent = 1; + shape[i].stride = 0; + } + const halide_type_t im_type = im.type(); + if (!check(im_type.code >= 0 && im_type.code < 3, "Unsupported image type")) { return false; } - offset += width * height * depth * bytes_per_element; - } - // Fill in the values for StripByteCounts - int32_t count = width * height * depth * bytes_per_element; - for (int32_t i = 0; i < channels; i++) { - if (!check(f.write_bytes(&count, sizeof(count)), "TIFF write failed")) { + const int32_t bytes_per_element = im_type.bytes(); + const int32_t width = shape[0].extent; + const int32_t height = shape[1].extent; + int32_t depth = shape[2].extent; + int32_t channels = shape[3].extent; + + if ((channels == 0 || channels == 1) && (depth < 5)) { + channels = depth; + depth = 1; + } + + // TIFF sample type values are: + // 0 => Signed int + // 1 => Unsigned int + // 2 => Floating-point + static const int16_t type_code_to_tiff_sample_type[] = { + 2, 1, 3 + }; + + struct halide_tiff_header header; + memset(&header, 0, sizeof(header)); + + const int32_t MMII = 0x4d4d4949; + // Select the appropriate two bytes signaling byte order automatically + const char* c = (const char*)&MMII; + header.byte_order_marker = (c[0] << 8) | c[1]; + header.version = 42; + header.ifd0_offset = offsetof(halide_tiff_header, entry_count); + header.entry_count = sizeof(header.entries) / sizeof(header.entries[0]); + + static_assert(sizeof(halide_tiff_tag) == 12, "Unexpected halide_tiff_tag packing"); + halide_tiff_tag* tag = &header.entries[0]; + tag++->assign32(256, 1, width); // ImageWidth + tag++->assign32(257, 1, height); // ImageLength + tag++->assign16(258, 1, int16_t(bytes_per_element * 8)); // BitsPerSample + tag++->assign16(259, 1, 1); // Compression -- none + tag++->assign16(262, 1, channels >= 3 ? 2 : 1); // PhotometricInterpretation -- black is zero or RGB + tag++->assign32(273, channels, sizeof(header)); // StripOffsets + tag++->assign16(277, 1, int16_t(channels)); // SamplesPerPixel + tag++->assign32(278, 1, height); // RowsPerStrip + tag++->assign32(279, channels, // StripByteCounts + (channels == 1) ? elements * bytes_per_element : sizeof(header) + channels * sizeof(int32_t)); // for channels > 1, this is an offset + tag++->assign32(282, 5, 1, + offsetof(halide_tiff_header, width_resolution)); // XResolution + tag++->assign32(283, 5, 1, + offsetof(halide_tiff_header, height_resolution)); // YResolution + tag++->assign16(284, 1, channels == 1 ? 1 : 2); // PlanarConfiguration -- contig or planar + tag++->assign16(296, 1, 1); // ResolutionUnit -- none + tag++->assign16(339, 1, type_code_to_tiff_sample_type[im_type.code]); // SampleFormat + tag++->assign32(32997, 1, depth); // Image depth + + // Verify we used exactly the number we declared + assert(tag == &header.entries[header.entry_count]); + + header.ifd0_end = 0; + header.width_resolution[0] = 1; + header.width_resolution[1] = 1; + header.height_resolution[0] = 1; + header.height_resolution[1] = 1; + + if (!check(f.write_bytes(&header, sizeof(header)), "TIFF write failed")) { return false; } - } - } - // If image is dense, we can write it in one fell swoop - if (elements * bytes_per_element == im.size_in_bytes()) { - if (!check(f.write_bytes(im.data(), im.size_in_bytes()), "TIFF write failed")) { - return false; - } - return true; - } + if (channels > 1) { + // Fill in the values for StripOffsets + int32_t offset = sizeof(header) + channels * sizeof(int32_t) * 2; + for (int32_t i = 0; i < channels; i++) { + if (!check(f.write_bytes(&offset, sizeof(offset)), "TIFF write failed")) { + return false; + } + offset += width * height * depth * bytes_per_element; + } + // Fill in the values for StripByteCounts + int32_t count = width * height * depth * bytes_per_element; + for (int32_t i = 0; i < channels; i++) { + if (!check(f.write_bytes(&count, sizeof(count)), "TIFF write failed")) { + return false; + } + } + } + + // If image is dense, we can write it in one fell swoop + if (elements * bytes_per_element == im.size_in_bytes()) { + if (!check(f.write_bytes(im.data(), im.size_in_bytes()), "TIFF write failed")) { + return false; + } + return true; + } - // Otherwise, write it out via manual traversal. + // Otherwise, write it out via manual traversal. #define HANDLE_CASE(CODE, BITS, TYPE) \ case halide_type_t(CODE, BITS).as_u32(): { \ ElemWriter ew(&f); \ @@ -1941,456 +2109,471 @@ bool save_tiff(ImageType &im, const std::string &filename) { break; \ } - switch (im_type.element_of().as_u32()) { - HANDLE_CASE(halide_type_float, 32, float) - HANDLE_CASE(halide_type_float, 64, double) - HANDLE_CASE(halide_type_int, 8, int8_t) - HANDLE_CASE(halide_type_int, 16, int16_t) - HANDLE_CASE(halide_type_int, 32, int32_t) - HANDLE_CASE(halide_type_int, 64, int64_t) - HANDLE_CASE(halide_type_uint, 1, bool) - HANDLE_CASE(halide_type_uint, 8, uint8_t) - HANDLE_CASE(halide_type_uint, 16, uint16_t) - HANDLE_CASE(halide_type_uint, 32, uint32_t) - HANDLE_CASE(halide_type_uint, 64, uint64_t) - // Note that we don't attempt to handle halide_type_handle here. - default: - assert(false && "Unsupported type"); - return false; - } + switch (im_type.element_of().as_u32()) { + HANDLE_CASE(halide_type_float, 32, float) + HANDLE_CASE(halide_type_float, 64, double) + HANDLE_CASE(halide_type_int, 8, int8_t) + HANDLE_CASE(halide_type_int, 16, int16_t) + HANDLE_CASE(halide_type_int, 32, int32_t) + HANDLE_CASE(halide_type_int, 64, int64_t) + HANDLE_CASE(halide_type_uint, 1, bool) + HANDLE_CASE(halide_type_uint, 8, uint8_t) + HANDLE_CASE(halide_type_uint, 16, uint16_t) + HANDLE_CASE(halide_type_uint, 32, uint32_t) + HANDLE_CASE(halide_type_uint, 64, uint64_t) + // Note that we don't attempt to handle halide_type_handle here. + default: + assert(false && "Unsupported type"); + return false; + } #undef HANDLE_CASE - return true; -} - -// Given something like ImageType, produce typedef ImageType -template -struct ImageTypeWithDynamicDims { - using type = decltype(std::declval().template as()); -}; - -// Given something like ImageType, produce typedef ImageType -template -struct ImageTypeWithElemType { - using type = decltype(std::declval().template as()); -}; - -// Given something like ImageType, produce typedef ImageType -template -struct ImageTypeWithConstElemType { - using type = decltype(std::declval().template as::type, AnyDims>()); -}; - -template -struct ImageIO { - using ConstImageType = typename ImageTypeWithConstElemType::type; - - std::function load; - std::function save; - std::function &()> query; -}; - -template -bool find_imageio(const std::string &filename, ImageIO *result) { - static_assert(!ImageType::has_static_halide_type, ""); - using ConstImageType = typename ImageTypeWithConstElemType::type; - - const std::map> m = { + return true; + } + + // Given something like ImageType, produce typedef ImageType + template + struct ImageTypeWithDynamicDims { + using type = decltype(std::declval().template as()); + }; + + // Given something like ImageType, produce typedef ImageType + template + struct ImageTypeWithElemType { + using type = decltype(std::declval().template as()); + }; + + // Given something like ImageType, produce typedef ImageType + template + struct ImageTypeWithConstElemType { + using type = decltype(std::declval().template as::type, AnyDims>()); + }; + + template + struct ImageIO { + using ConstImageType = typename ImageTypeWithConstElemType::type; + + std::function load; + std::function save; + std::function&()> query; + }; + + template + bool find_imageio(const std::string& filename, ImageIO* result) + { + static_assert(!ImageType::has_static_halide_type, ""); + using ConstImageType = typename ImageTypeWithConstElemType::type; + + const std::map> m = { #ifndef HALIDE_NO_JPEG - {"jpeg", {load_jpg, save_jpg, query_jpg}}, - {"jpg", {load_jpg, save_jpg, query_jpg}}, + { "jpeg", { load_jpg, save_jpg, query_jpg } }, + { "jpg", { load_jpg, save_jpg, query_jpg } }, #endif - {"pgm", {load_pgm, save_pgm, query_pgm}}, + { "pgm", { load_pgm, save_pgm, query_pgm } }, #ifndef HALIDE_NO_PNG - {"png", {load_png, save_png, query_png}}, + { "png", { load_png, save_png, query_png } }, #endif - {"ppm", {load_ppm, save_ppm, query_ppm}}, - {"tmp", {load_tmp, save_tmp, query_tmp}}, - {"mat", {load_mat, save_mat, query_mat}}, - {"tiff", {load_tiff, save_tiff, query_tiff}}, - }; - std::string ext = Internal::get_lowercase_extension(filename); - auto it = m.find(ext); - if (it != m.end()) { - *result = it->second; - return true; - } + { "ppm", { load_ppm, save_ppm, query_ppm } }, + { "tmp", { load_tmp, save_tmp, query_tmp } }, + { "mat", { load_mat, save_mat, query_mat } }, + { "tiff", { load_tiff, save_tiff, query_tiff } }, + }; + std::string ext = Internal::get_lowercase_extension(filename); + auto it = m.find(ext); + if (it != m.end()) { + *result = it->second; + return true; + } - std::string err = "unsupported file extension \"" + ext + "\", supported are:"; - for (auto &it : m) { - err += " " + it.first; - } - err += "\n"; - return check(false, err.c_str()); -} - -template -FormatInfo best_save_format(const ImageType &im, const std::set &info) { - // A bit ad hoc, but will do for now: - // Perfect score is zero (exact match). - // The larger the score, the worse the match. - int best_score = 0x7fffffff; - FormatInfo best{}; - const halide_type_t im_type = im.type(); - const int im_dimensions = im.dimensions(); - for (const auto &f : info) { - int score = 0; - // If format has too-few dimensions, that's very bad. - score += std::max(0, im_dimensions - f.dimensions) * 1024; - // If format has too-few bits, that's pretty bad. - score += std::max(0, im_type.bits - f.type.bits) * 8; - // If format has too-many bits, that's a little bad. - score += std::max(0, f.type.bits - im_type.bits); - // If format has different code, that's a little bad. - score += (f.type.code != im_type.code) ? 1 : 0; - if (score < best_score) { - best_score = score; - best = f; + std::string err = "unsupported file extension \"" + ext + "\", supported are:"; + for (auto& it : m) { + err += " " + it.first; + } + err += "\n"; + return check(false, err.c_str()); } - } - - return best; -} -} // namespace Internal + template + FormatInfo best_save_format(const ImageType& im, const std::set& info) + { + // A bit ad hoc, but will do for now: + // Perfect score is zero (exact match). + // The larger the score, the worse the match. + int best_score = 0x7fffffff; + FormatInfo best {}; + const halide_type_t im_type = im.type(); + const int im_dimensions = im.dimensions(); + for (const auto& f : info) { + int score = 0; + // If format has too-few dimensions, that's very bad. + score += std::max(0, im_dimensions - f.dimensions) * 1024; + // If format has too-few bits, that's pretty bad. + score += std::max(0, im_type.bits - f.type.bits) * 8; + // If format has too-many bits, that's a little bad. + score += std::max(0, f.type.bits - im_type.bits); + // If format has different code, that's a little bad. + score += (f.type.code != im_type.code) ? 1 : 0; + if (score < best_score) { + best_score = score; + best = f; + } + } -struct ImageTypeConversion { - // Convert an Image from one ElemType to another, where the src and - // dst types are statically known (e.g. Buffer -> Buffer). - // Note that this does conversion with scaling -- intepreting integers - // as fixed-point numbers between 0 and 1 -- not merely C-style casting. - // - // You'd normally call this with an explicit type for DstElemType and - // allow ImageType to be inferred, e.g. - // Buffer src = ...; - // Buffer dst = convert_image(src); - template::value>::type * = nullptr> - static auto convert_image(const ImageType &src) -> - typename Internal::ImageTypeWithElemType::type { - // The enable_if ensures this will never fire; this is here primarily - // as documentation and a backstop against breakage. - static_assert(ImageType::has_static_halide_type, - "This variant of convert_image() requires a statically-typed image"); - - using SrcImageType = ImageType; - using SrcElemType = typename SrcImageType::ElemType; - - using DstImageType = typename Internal::ImageTypeWithElemType::type; - - DstImageType dst = DstImageType::make_with_shape_of(src); - const auto converter = [](DstElemType &dst_elem, SrcElemType src_elem) { - dst_elem = Internal::convert(src_elem); - }; - dst.for_each_value(converter, src); - dst.set_host_dirty(); + return best; + } - return dst; - } + } // namespace Internal + + struct ImageTypeConversion { + // Convert an Image from one ElemType to another, where the src and + // dst types are statically known (e.g. Buffer -> Buffer). + // Note that this does conversion with scaling -- intepreting integers + // as fixed-point numbers between 0 and 1 -- not merely C-style casting. + // + // You'd normally call this with an explicit type for DstElemType and + // allow ImageType to be inferred, e.g. + // Buffer src = ...; + // Buffer dst = convert_image(src); + template ::value>::type* = nullptr> + static auto convert_image(const ImageType& src) -> + typename Internal::ImageTypeWithElemType::type + { + // The enable_if ensures this will never fire; this is here primarily + // as documentation and a backstop against breakage. + static_assert(ImageType::has_static_halide_type, + "This variant of convert_image() requires a statically-typed image"); + + using SrcImageType = ImageType; + using SrcElemType = typename SrcImageType::ElemType; - // Convert an Image from one ElemType to another, where the dst type is statically - // known but the src type is not (e.g. Buffer<> -> Buffer). - // You'd normally call this with an explicit type for DstElemType and - // allow ImageType to be inferred, e.g. - // Buffer src = ...; - // Buffer dst = convert_image(src); - template::value>::type * = nullptr> - static auto convert_image(const ImageType &src) -> - typename Internal::ImageTypeWithElemType::type { - // The enable_if ensures this will never fire; this is here primarily - // as documentation and a backstop against breakage. - static_assert(!ImageType::has_static_halide_type, - "This variant of convert_image() requires a dynamically-typed image"); - constexpr int AnyDims = Internal::AnyDims; - - const halide_type_t src_type = src.type(); - switch (src_type.element_of().as_u32()) { - case halide_type_t(halide_type_float, 32).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_float, 64).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_int, 8).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_int, 16).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_int, 32).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_int, 64).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_uint, 1).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_uint, 8).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_uint, 16).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_uint, 32).as_u32(): - return convert_image(src.template as()); - case halide_type_t(halide_type_uint, 64).as_u32(): - return convert_image(src.template as()); - default: - assert(false && "Unsupported type"); using DstImageType = typename Internal::ImageTypeWithElemType::type; - return DstImageType(); - } - } - // Convert an Image from one ElemType to another, where the src type - // is statically known but the dst type is not - // (e.g. Buffer -> Buffer<>(halide_type_t)). - template::value>::type * = nullptr> - static auto convert_image(const ImageType &src, const halide_type_t &dst_type) -> - typename Internal::ImageTypeWithElemType::type { - // The enable_if ensures this will never fire; this is here primarily - // as documentation and a backstop against breakage. - static_assert(ImageType::has_static_halide_type, - "This variant of convert_image() requires a statically-typed image"); - - // Call the appropriate static-to-static conversion routine - // based on the desired dst type. - switch (dst_type.element_of().as_u32()) { - case halide_type_t(halide_type_float, 32).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_float, 64).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_int, 8).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_int, 16).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_int, 32).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_int, 64).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_uint, 1).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_uint, 8).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_uint, 16).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_uint, 32).as_u32(): - return convert_image(src); - case halide_type_t(halide_type_uint, 64).as_u32(): - return convert_image(src); - default: - assert(false && "Unsupported type"); - using RetImageType = typename Internal::ImageTypeWithDynamicDims::type; - return RetImageType(); + DstImageType dst = DstImageType::make_with_shape_of(src); + const auto converter = [](DstElemType& dst_elem, SrcElemType src_elem) { + dst_elem = Internal::convert(src_elem); + }; + dst.for_each_value(converter, src); + dst.set_host_dirty(); + + return dst; } - } - // Convert an Image from one ElemType to another, where neither src type - // nor dst type are statically known - // (e.g. Buffer<>(halide_type_t) -> Buffer<>(halide_type_t)). - template::value>::type * = nullptr> - static auto convert_image(const ImageType &src, const halide_type_t &dst_type) -> - typename Internal::ImageTypeWithElemType::type { - // The enable_if ensures this will never fire; this is here primarily - // as documentation and a backstop against breakage. - static_assert(!ImageType::has_static_halide_type, - "This variant of convert_image() requires a dynamically-typed image"); - constexpr int AnyDims = Internal::AnyDims; - - // Sniff the runtime type of src, coerce it to that type using as<>(), - // and call the static-to-dynamic variant of this method. (Note that - // this forces instantiation of the complete any-to-any conversion - // matrix of code.) - const halide_type_t src_type = src.type(); - switch (src_type.element_of().as_u32()) { - case halide_type_t(halide_type_float, 32).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_float, 64).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_int, 8).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_int, 16).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_int, 32).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_int, 64).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_uint, 1).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_uint, 8).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_uint, 16).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_uint, 32).as_u32(): - return convert_image(src.template as(), dst_type); - case halide_type_t(halide_type_uint, 64).as_u32(): - return convert_image(src.template as(), dst_type); - default: - assert(false && "Unsupported type"); - using RetImageType = typename Internal::ImageTypeWithDynamicDims::type; - return RetImageType(); + // Convert an Image from one ElemType to another, where the dst type is statically + // known but the src type is not (e.g. Buffer<> -> Buffer). + // You'd normally call this with an explicit type for DstElemType and + // allow ImageType to be inferred, e.g. + // Buffer src = ...; + // Buffer dst = convert_image(src); + template ::value>::type* = nullptr> + static auto convert_image(const ImageType& src) -> + typename Internal::ImageTypeWithElemType::type + { + // The enable_if ensures this will never fire; this is here primarily + // as documentation and a backstop against breakage. + static_assert(!ImageType::has_static_halide_type, + "This variant of convert_image() requires a dynamically-typed image"); + constexpr int AnyDims = Internal::AnyDims; + + const halide_type_t src_type = src.type(); + switch (src_type.element_of().as_u32()) { + case halide_type_t(halide_type_float, 32).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_float, 64).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_int, 8).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_int, 16).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_int, 32).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_int, 64).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_uint, 1).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_uint, 8).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_uint, 16).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_uint, 32).as_u32(): + return convert_image(src.template as()); + case halide_type_t(halide_type_uint, 64).as_u32(): + return convert_image(src.template as()); + default: + assert(false && "Unsupported type"); + using DstImageType = typename Internal::ImageTypeWithElemType::type; + return DstImageType(); + } } - } -}; - -// Load the Image from the given file. -// If output Image has a static type, and the loaded image cannot be stored -// in such an image without losing data, fail. -// Returns false upon failure. -template -bool load(const std::string &filename, ImageType *im) { - using DynamicImageType = typename Internal::ImageTypeWithElemType::type; - Internal::ImageIO imageio; - if (!Internal::find_imageio(filename, &imageio)) { - return false; - } - using DynamicImageType = typename Internal::ImageTypeWithElemType::type; - DynamicImageType im_d; - if (!imageio.load(filename, &im_d)) { - return false; - } - // Allow statically-typed images to be passed as the out-param, but do - // a runtime check to ensure - if (ImageType::has_static_halide_type) { - const halide_type_t expected_type = ImageType::static_halide_type(); - if (!check(im_d.type() == expected_type, "Image loaded did not match the expected type")) { - return false; + + // Convert an Image from one ElemType to another, where the src type + // is statically known but the dst type is not + // (e.g. Buffer -> Buffer<>(halide_type_t)). + template ::value>::type* = nullptr> + static auto convert_image(const ImageType& src, const halide_type_t& dst_type) -> + typename Internal::ImageTypeWithElemType::type + { + // The enable_if ensures this will never fire; this is here primarily + // as documentation and a backstop against breakage. + static_assert(ImageType::has_static_halide_type, + "This variant of convert_image() requires a statically-typed image"); + + // Call the appropriate static-to-static conversion routine + // based on the desired dst type. + switch (dst_type.element_of().as_u32()) { + case halide_type_t(halide_type_float, 32).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_float, 64).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_int, 8).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_int, 16).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_int, 32).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_int, 64).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_uint, 1).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_uint, 8).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_uint, 16).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_uint, 32).as_u32(): + return convert_image(src); + case halide_type_t(halide_type_uint, 64).as_u32(): + return convert_image(src); + default: + assert(false && "Unsupported type"); + using RetImageType = typename Internal::ImageTypeWithDynamicDims::type; + return RetImageType(); + } } - } - *im = im_d.template as(); - im->set_host_dirty(); - return true; -} - -// Save the Image in the format associated with the filename's extension. -// If the format can't represent the Image without losing data, fail. -// Returns false upon failure. -template -bool save(ImageType &im, const std::string &filename) { - using DynamicImageType = typename Internal::ImageTypeWithElemType::type; - Internal::ImageIO imageio; - if (!Internal::find_imageio(filename, &imageio)) { - return false; - } - if (!check(imageio.query().count({im.type(), im.dimensions()}) > 0, "Image cannot be saved in this format")) { - return false; - } - // Allow statically-typed images to be passed in, but quietly pass them on - // as dynamically-typed images. - auto im_d = im.template as(); - return imageio.save(im_d, filename); -} - -// Return a set of FormatInfo structs that contain the legal type-and-dimensions -// that can be saved in this format. Most applications won't ever need to use -// this call. Returns false upon failure. -template -bool save_query(const std::string &filename, std::set *info) { - using DynamicImageType = typename Internal::ImageTypeWithElemType::type; - Internal::ImageIO imageio; - if (!Internal::find_imageio(filename, &imageio)) { - return false; - } - *info = imageio.query(); - return true; -} - -// Fancy wrapper to call load() with CheckFail, inferring the return type; -// this allows you to simply use -// -// Image im = load_image("filename"); -// -// without bothering to check error results (all errors simply abort). -// -// Note that if the image being loaded doesn't match the static type and -// dimensions of of the image on the LHS, a runtime error will occur. -class load_image { -public: - load_image(const std::string &f) - : filename(f) { - } + // Convert an Image from one ElemType to another, where neither src type + // nor dst type are statically known + // (e.g. Buffer<>(halide_type_t) -> Buffer<>(halide_type_t)). + template ::value>::type* = nullptr> + static auto convert_image(const ImageType& src, const halide_type_t& dst_type) -> + typename Internal::ImageTypeWithElemType::type + { + // The enable_if ensures this will never fire; this is here primarily + // as documentation and a backstop against breakage. + static_assert(!ImageType::has_static_halide_type, + "This variant of convert_image() requires a dynamically-typed image"); + constexpr int AnyDims = Internal::AnyDims; + + // Sniff the runtime type of src, coerce it to that type using as<>(), + // and call the static-to-dynamic variant of this method. (Note that + // this forces instantiation of the complete any-to-any conversion + // matrix of code.) + const halide_type_t src_type = src.type(); + switch (src_type.element_of().as_u32()) { + case halide_type_t(halide_type_float, 32).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_float, 64).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_int, 8).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_int, 16).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_int, 32).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_int, 64).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_uint, 1).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_uint, 8).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_uint, 16).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_uint, 32).as_u32(): + return convert_image(src.template as(), dst_type); + case halide_type_t(halide_type_uint, 64).as_u32(): + return convert_image(src.template as(), dst_type); + default: + assert(false && "Unsupported type"); + using RetImageType = typename Internal::ImageTypeWithDynamicDims::type; + return RetImageType(); + } + } + }; - template - operator ImageType() { + // Load the Image from the given file. + // If output Image has a static type, and the loaded image cannot be stored + // in such an image without losing data, fail. + // Returns false upon failure. + template + bool load(const std::string& filename, ImageType* im) + { + using DynamicImageType = typename Internal::ImageTypeWithElemType::type; + Internal::ImageIO imageio; + if (!Internal::find_imageio(filename, &imageio)) { + return false; + } using DynamicImageType = typename Internal::ImageTypeWithElemType::type; DynamicImageType im_d; - (void)load(filename, &im_d); - Internal::CheckFail(ImageType::can_convert_from(im_d), - "Type mismatch assigning the result of load_image. " - "Did you mean to use load_and_convert_image?"); - return im_d.template as(); + if (!imageio.load(filename, &im_d)) { + return false; + } + // Allow statically-typed images to be passed as the out-param, but do + // a runtime check to ensure + if (ImageType::has_static_halide_type) { + const halide_type_t expected_type = ImageType::static_halide_type(); + if (!check(im_d.type() == expected_type, "Image loaded did not match the expected type")) { + return false; + } + } + *im = im_d.template as(); + im->set_host_dirty(); + return true; } -private: - const std::string filename; -}; + // Save the Image in the format associated with the filename's extension. + // If the format can't represent the Image without losing data, fail. + // Returns false upon failure. + template + bool save(ImageType& im, const std::string& filename) + { + using DynamicImageType = typename Internal::ImageTypeWithElemType::type; + Internal::ImageIO imageio; + if (!Internal::find_imageio(filename, &imageio)) { + return false; + } + if (!check(imageio.query().count({ im.type(), im.dimensions() }) > 0, "Image cannot be saved in this format")) { + return false; + } -// Like load_image, but quietly convert the loaded image to the type of the LHS -// if necessary, discarding information if necessary. -class load_and_convert_image { -public: - load_and_convert_image(const std::string &f) - : filename(f) { + // Allow statically-typed images to be passed in, but quietly pass them on + // as dynamically-typed images. + auto im_d = im.template as(); + return imageio.save(im_d, filename); } - template - inline operator ImageType() { + // Return a set of FormatInfo structs that contain the legal type-and-dimensions + // that can be saved in this format. Most applications won't ever need to use + // this call. Returns false upon failure. + template + bool save_query(const std::string& filename, std::set* info) + { using DynamicImageType = typename Internal::ImageTypeWithElemType::type; - DynamicImageType im_d; - (void)load(filename, &im_d); - const halide_type_t expected_type = ImageType::static_halide_type(); - if (im_d.type() == expected_type) { - return im_d.template as(); - } else { - return ImageTypeConversion::convert_image(im_d); + Internal::ImageIO imageio; + if (!Internal::find_imageio(filename, &imageio)) { + return false; } + *info = imageio.query(); + return true; } -private: - const std::string filename; -}; - -// Fancy wrapper to call save() with CheckFail; this allows you to simply use -// -// save_image(im, "filename"); -// -// without bothering to check error results (all errors simply abort). -// -// If the specified image file format cannot represent the image without -// losing data (e.g, a float32 or 4-dimensional image saved as a JPEG), -// a runtime error will occur. -template -void save_image(ImageType &im, const std::string &filename) { - auto im_d = im.template as(); - (void)save(im_d, filename); -} - -// Like save_image, but quietly convert the saved image to a type that the -// specified image file format can hold, discarding information if necessary. -// (Note that the input image is unaffected!) -template -void convert_and_save_image(ImageType &im, const std::string &filename) { - // We'll be doing any conversion on the CPU - if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { - return; - } + // Fancy wrapper to call load() with CheckFail, inferring the return type; + // this allows you to simply use + // + // Image im = load_image("filename"); + // + // without bothering to check error results (all errors simply abort). + // + // Note that if the image being loaded doesn't match the static type and + // dimensions of of the image on the LHS, a runtime error will occur. + class load_image { + public: + load_image(const std::string& f) + : filename(f) + { + } - std::set info; - (void)save_query::type, check>(filename, &info); - const FormatInfo best = Internal::best_save_format(im, info); - if (best.type == im.type() && best.dimensions == im.dimensions()) { - // It's an exact match, we can save as-is. - using DynamicImageDims = typename Internal::ImageTypeWithDynamicDims::type; - (void)save(im.template as(), filename); - } else { - using DynamicImageType = typename Internal::ImageTypeWithElemType::type; - DynamicImageType im_converted = ImageTypeConversion::convert_image(im, best.type); - while (im_converted.dimensions() < best.dimensions) { - im_converted.add_dimension(); + template + operator ImageType() + { + using DynamicImageType = typename Internal::ImageTypeWithElemType::type; + DynamicImageType im_d; + (void)load(filename, &im_d); + Internal::CheckFail(ImageType::can_convert_from(im_d), + "Type mismatch assigning the result of load_image. " + "Did you mean to use load_and_convert_image?"); + return im_d.template as(); + } + + private: + const std::string filename; + }; + + // Like load_image, but quietly convert the loaded image to the type of the LHS + // if necessary, discarding information if necessary. + class load_and_convert_image { + public: + load_and_convert_image(const std::string& f) + : filename(f) + { + } + + template + inline operator ImageType() + { + using DynamicImageType = typename Internal::ImageTypeWithElemType::type; + DynamicImageType im_d; + (void)load(filename, &im_d); + const halide_type_t expected_type = ImageType::static_halide_type(); + if (im_d.type() == expected_type) { + return im_d.template as(); + } else { + return ImageTypeConversion::convert_image(im_d); + } + } + + private: + const std::string filename; + }; + + // Fancy wrapper to call save() with CheckFail; this allows you to simply use + // + // save_image(im, "filename"); + // + // without bothering to check error results (all errors simply abort). + // + // If the specified image file format cannot represent the image without + // losing data (e.g, a float32 or 4-dimensional image saved as a JPEG), + // a runtime error will occur. + template + void save_image(ImageType& im, const std::string& filename) + { + auto im_d = im.template as(); + (void)save(im_d, filename); + } + + // Like save_image, but quietly convert the saved image to a type that the + // specified image file format can hold, discarding information if necessary. + // (Note that the input image is unaffected!) + template + void convert_and_save_image(ImageType& im, const std::string& filename) + { + // We'll be doing any conversion on the CPU + if (!check(im.copy_to_host() == halide_error_code_success, "copy_to_host() failed.")) { + return; + } + + std::set info; + (void)save_query::type, check>(filename, &info); + const FormatInfo best = Internal::best_save_format(im, info); + if (best.type == im.type() && best.dimensions == im.dimensions()) { + // It's an exact match, we can save as-is. + using DynamicImageDims = typename Internal::ImageTypeWithDynamicDims::type; + (void)save(im.template as(), filename); + } else { + using DynamicImageType = typename Internal::ImageTypeWithElemType::type; + DynamicImageType im_converted = ImageTypeConversion::convert_image(im, best.type); + while (im_converted.dimensions() < best.dimensions) { + im_converted.add_dimension(); + } + (void)save(im_converted, filename); } - (void)save(im_converted, filename); } -} -} // namespace Tools -} // namespace Halide +} // namespace Tools +} // namespace Halide -#endif // HALIDE_IMAGE_IO_H \ No newline at end of file +#endif // HALIDE_IMAGE_IO_H \ No newline at end of file diff --git a/src/bb/image-io/httplib.h b/src/bb/image-io/httplib.h index d1d8486b..8c87cd88 100644 --- a/src/bb/image-io/httplib.h +++ b/src/bb/image-io/httplib.h @@ -81,10 +81,8 @@ #endif #ifndef CPPHTTPLIB_THREAD_POOL_COUNT -#define CPPHTTPLIB_THREAD_POOL_COUNT \ - ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ - ? std::thread::hardware_concurrency() - 1 \ - : 0)) +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() - 1 : 0)) #endif /* @@ -113,11 +111,11 @@ using ssize_t = int; #endif // _MSC_VER #ifndef S_ISREG -#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG) +#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG) #endif // S_ISREG #ifndef S_ISDIR -#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) #endif // S_ISDIR #ifndef NOMINMAX @@ -215,8 +213,9 @@ using socket_t = int; #if OPENSSL_VERSION_NUMBER < 0x10100000L #include -inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { - return M_ASN1_STRING_data(asn1); +inline const unsigned char* ASN1_STRING_get0_data(const ASN1_STRING* asn1) +{ + return M_ASN1_STRING_data(asn1); } #endif #endif @@ -237,13 +236,14 @@ namespace httplib { namespace detail { -struct ci { - bool operator()(const std::string &s1, const std::string &s2) const { - return std::lexicographical_compare( - s1.begin(), s1.end(), s2.begin(), s2.end(), - [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); - } -}; + struct ci { + bool operator()(const std::string& s1, const std::string& s2) const + { + return std::lexicographical_compare( + s1.begin(), s1.end(), s2.begin(), s2.end(), + [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); + } + }; } // namespace detail @@ -255,951 +255,973 @@ using Match = std::smatch; using Progress = std::function; struct Response; -using ResponseHandler = std::function; +using ResponseHandler = std::function; struct MultipartFormData { - std::string name; - std::string content; - std::string filename; - std::string content_type; + std::string name; + std::string content; + std::string filename; + std::string content_type; }; using MultipartFormDataItems = std::vector; using MultipartFormDataMap = std::multimap; class DataSink { public: - DataSink() : os(&sb_), sb_(*this) {} + DataSink() + : os(&sb_) + , sb_(*this) + { + } - DataSink(const DataSink &) = delete; - DataSink &operator=(const DataSink &) = delete; - DataSink(DataSink &&) = delete; - DataSink &operator=(DataSink &&) = delete; + DataSink(const DataSink&) = delete; + DataSink& operator=(const DataSink&) = delete; + DataSink(DataSink&&) = delete; + DataSink& operator=(DataSink&&) = delete; - std::function write; - std::function done; - std::function is_writable; - std::ostream os; + std::function write; + std::function done; + std::function is_writable; + std::ostream os; private: - class data_sink_streambuf : public std::streambuf { - public: - explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} + class data_sink_streambuf : public std::streambuf { + public: + explicit data_sink_streambuf(DataSink& sink) + : sink_(sink) + { + } - protected: - std::streamsize xsputn(const char *s, std::streamsize n) { - sink_.write(s, static_cast(n)); - return n; - } + protected: + std::streamsize xsputn(const char* s, std::streamsize n) + { + sink_.write(s, static_cast(n)); + return n; + } - private: - DataSink &sink_; - }; + private: + DataSink& sink_; + }; - data_sink_streambuf sb_; + data_sink_streambuf sb_; }; -using ContentProvider = - std::function; +using ContentProvider = std::function; -using ContentProviderWithoutLength = - std::function; +using ContentProviderWithoutLength = std::function; -using ContentReceiver = - std::function; +using ContentReceiver = std::function; -using MultipartContentHeader = - std::function; +using MultipartContentHeader = std::function; class ContentReader { public: - using Reader = std::function; - using MultipartReader = std::function; + using Reader = std::function; + using MultipartReader = std::function; - ContentReader(Reader reader, MultipartReader multipart_reader) - : reader_(reader), multipart_reader_(multipart_reader) {} + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(reader) + , multipart_reader_(multipart_reader) + { + } - bool operator()(MultipartContentHeader header, - ContentReceiver receiver) const { - return multipart_reader_(header, receiver); - } + bool operator()(MultipartContentHeader header, + ContentReceiver receiver) const + { + return multipart_reader_(header, receiver); + } - bool operator()(ContentReceiver receiver) const { return reader_(receiver); } + bool operator()(ContentReceiver receiver) const { return reader_(receiver); } - Reader reader_; - MultipartReader multipart_reader_; + Reader reader_; + MultipartReader multipart_reader_; }; using Range = std::pair; using Ranges = std::vector; struct Request { - std::string method; - std::string path; - Headers headers; - std::string body; - - std::string remote_addr; - int remote_port = -1; - - // for server - std::string version; - std::string target; - Params params; - MultipartFormDataMap files; - Ranges ranges; - Match matches; - - // for client - size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; - ResponseHandler response_handler; - ContentReceiver content_receiver; - size_t content_length = 0; - ContentProvider content_provider; - Progress progress; + std::string method; + std::string path; + Headers headers; + std::string body; + + std::string remote_addr; + int remote_port = -1; + + // for server + std::string version; + std::string target; + Params params; + MultipartFormDataMap files; + Ranges ranges; + Match matches; + + // for client + size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; + ResponseHandler response_handler; + ContentReceiver content_receiver; + size_t content_length = 0; + ContentProvider content_provider; + Progress progress; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - const SSL *ssl; + const SSL* ssl; #endif - bool has_header(const char *key) const; - std::string get_header_value(const char *key, size_t id = 0) const; - template - T get_header_value(const char *key, size_t id = 0) const; - size_t get_header_value_count(const char *key) const; - void set_header(const char *key, const char *val); - void set_header(const char *key, const std::string &val); + bool has_header(const char* key) const; + std::string get_header_value(const char* key, size_t id = 0) const; + template + T get_header_value(const char* key, size_t id = 0) const; + size_t get_header_value_count(const char* key) const; + void set_header(const char* key, const char* val); + void set_header(const char* key, const std::string& val); - bool has_param(const char *key) const; - std::string get_param_value(const char *key, size_t id = 0) const; - size_t get_param_value_count(const char *key) const; + bool has_param(const char* key) const; + std::string get_param_value(const char* key, size_t id = 0) const; + size_t get_param_value_count(const char* key) const; - bool is_multipart_form_data() const; + bool is_multipart_form_data() const; - bool has_file(const char *key) const; - MultipartFormData get_file_value(const char *key) const; + bool has_file(const char* key) const; + MultipartFormData get_file_value(const char* key) const; - // private members... - size_t authorization_count_ = 0; + // private members... + size_t authorization_count_ = 0; }; struct Response { - std::string version; - int status = -1; - std::string reason; - Headers headers; - std::string body; - - bool has_header(const char *key) const; - std::string get_header_value(const char *key, size_t id = 0) const; - template - T get_header_value(const char *key, size_t id = 0) const; - size_t get_header_value_count(const char *key) const; - void set_header(const char *key, const char *val); - void set_header(const char *key, const std::string &val); - - void set_redirect(const char *url, int status = 302); - void set_redirect(const std::string &url, int status = 302); - void set_content(const char *s, size_t n, const char *content_type); - void set_content(std::string s, const char *content_type); - - void set_content_provider( - size_t length, const char *content_type, ContentProvider provider, - const std::function &resource_releaser = nullptr); - - void set_content_provider( - const char *content_type, ContentProviderWithoutLength provider, - const std::function &resource_releaser = nullptr); - - void set_chunked_content_provider( - const char *content_type, ContentProviderWithoutLength provider, - const std::function &resource_releaser = nullptr); - - Response() = default; - Response(const Response &) = default; - Response &operator=(const Response &) = default; - Response(Response &&) = default; - Response &operator=(Response &&) = default; - ~Response() { - if (content_provider_resource_releaser_) { - content_provider_resource_releaser_(); - } - } - - // private members... - size_t content_length_ = 0; - ContentProvider content_provider_; - std::function content_provider_resource_releaser_; - bool is_chunked_content_provider = false; + std::string version; + int status = -1; + std::string reason; + Headers headers; + std::string body; + + bool has_header(const char* key) const; + std::string get_header_value(const char* key, size_t id = 0) const; + template + T get_header_value(const char* key, size_t id = 0) const; + size_t get_header_value_count(const char* key) const; + void set_header(const char* key, const char* val); + void set_header(const char* key, const std::string& val); + + void set_redirect(const char* url, int status = 302); + void set_redirect(const std::string& url, int status = 302); + void set_content(const char* s, size_t n, const char* content_type); + void set_content(std::string s, const char* content_type); + + void set_content_provider( + size_t length, const char* content_type, ContentProvider provider, + const std::function& resource_releaser = nullptr); + + void set_content_provider( + const char* content_type, ContentProviderWithoutLength provider, + const std::function& resource_releaser = nullptr); + + void set_chunked_content_provider( + const char* content_type, ContentProviderWithoutLength provider, + const std::function& resource_releaser = nullptr); + + Response() = default; + Response(const Response&) = default; + Response& operator=(const Response&) = default; + Response(Response&&) = default; + Response& operator=(Response&&) = default; + ~Response() + { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + std::function content_provider_resource_releaser_; + bool is_chunked_content_provider = false; }; class Stream { public: - virtual ~Stream() = default; + virtual ~Stream() = default; - virtual bool is_readable() const = 0; - virtual bool is_writable() const = 0; + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; - virtual ssize_t read(char *ptr, size_t size) = 0; - virtual ssize_t write(const char *ptr, size_t size) = 0; - virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + virtual ssize_t read(char* ptr, size_t size) = 0; + virtual ssize_t write(const char* ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string& ip, int& port) const = 0; - template - ssize_t write_format(const char *fmt, const Args &... args); - ssize_t write(const char *ptr); - ssize_t write(const std::string &s); + template + ssize_t write_format(const char* fmt, const Args&... args); + ssize_t write(const char* ptr); + ssize_t write(const std::string& s); }; class TaskQueue { public: - TaskQueue() = default; - virtual ~TaskQueue() = default; + TaskQueue() = default; + virtual ~TaskQueue() = default; - virtual void enqueue(std::function fn) = 0; - virtual void shutdown() = 0; + virtual void enqueue(std::function fn) = 0; + virtual void shutdown() = 0; - virtual void on_idle(){}; + virtual void on_idle() {}; }; class ThreadPool : public TaskQueue { public: - explicit ThreadPool(size_t n) : shutdown_(false) { - while (n) { - threads_.emplace_back(worker(*this)); - n--; + explicit ThreadPool(size_t n) + : shutdown_(false) + { + while (n) { + threads_.emplace_back(worker(*this)); + n--; + } } - } - ThreadPool(const ThreadPool &) = delete; - ~ThreadPool() override = default; + ThreadPool(const ThreadPool&) = delete; + ~ThreadPool() override = default; - void enqueue(std::function fn) override { - std::unique_lock lock(mutex_); - jobs_.push_back(fn); - cond_.notify_one(); - } - - void shutdown() override { - // Stop all worker threads... + void enqueue(std::function fn) override { - std::unique_lock lock(mutex_); - shutdown_ = true; + std::unique_lock lock(mutex_); + jobs_.push_back(fn); + cond_.notify_one(); } - cond_.notify_all(); + void shutdown() override + { + // Stop all worker threads... + { + std::unique_lock lock(mutex_); + shutdown_ = true; + } + + cond_.notify_all(); - // Join... - for (auto &t : threads_) { - t.join(); + // Join... + for (auto& t : threads_) { + t.join(); + } } - } private: - struct worker { - explicit worker(ThreadPool &pool) : pool_(pool) {} + struct worker { + explicit worker(ThreadPool& pool) + : pool_(pool) + { + } - void operator()() { - for (;;) { - std::function fn; + void operator()() { - std::unique_lock lock(pool_.mutex_); + for (;;) { + std::function fn; + { + std::unique_lock lock(pool_.mutex_); - pool_.cond_.wait( - lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); + pool_.cond_.wait( + lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); - if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } + if (pool_.shutdown_ && pool_.jobs_.empty()) { + break; + } - fn = pool_.jobs_.front(); - pool_.jobs_.pop_front(); - } + fn = pool_.jobs_.front(); + pool_.jobs_.pop_front(); + } - assert(true == static_cast(fn)); - fn(); - } - } + assert(true == static_cast(fn)); + fn(); + } + } - ThreadPool &pool_; - }; - friend struct worker; + ThreadPool& pool_; + }; + friend struct worker; - std::vector threads_; - std::list> jobs_; + std::vector threads_; + std::list> jobs_; - bool shutdown_; + bool shutdown_; - std::condition_variable cond_; - std::mutex mutex_; + std::condition_variable cond_; + std::mutex mutex_; }; -using Logger = std::function; +using Logger = std::function; using SocketOptions = std::function; -inline void default_socket_options(socket_t sock) { - int yes = 1; +inline void default_socket_options(socket_t sock) +{ + int yes = 1; #ifdef _WIN32 - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), - sizeof(yes)); - setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - reinterpret_cast(&yes), sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), + sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast(&yes), sizeof(yes)); #else #ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&yes), - sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&yes), + sizeof(yes)); #else - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), - sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), + sizeof(yes)); #endif #endif } class Server { public: - using Handler = std::function; - using HandlerWithContentReader = std::function; - using Expect100ContinueHandler = - std::function; + using Handler = std::function; + using HandlerWithContentReader = std::function; + using Expect100ContinueHandler = std::function; - Server(); + Server(); - virtual ~Server(); + virtual ~Server(); - virtual bool is_valid() const; + virtual bool is_valid() const; - Server &Get(const char *pattern, Handler handler); - Server &Post(const char *pattern, Handler handler); - Server &Post(const char *pattern, HandlerWithContentReader handler); - Server &Put(const char *pattern, Handler handler); - Server &Put(const char *pattern, HandlerWithContentReader handler); - Server &Patch(const char *pattern, Handler handler); - Server &Patch(const char *pattern, HandlerWithContentReader handler); - Server &Delete(const char *pattern, Handler handler); - Server &Delete(const char *pattern, HandlerWithContentReader handler); - Server &Options(const char *pattern, Handler handler); + Server& Get(const char* pattern, Handler handler); + Server& Post(const char* pattern, Handler handler); + Server& Post(const char* pattern, HandlerWithContentReader handler); + Server& Put(const char* pattern, Handler handler); + Server& Put(const char* pattern, HandlerWithContentReader handler); + Server& Patch(const char* pattern, Handler handler); + Server& Patch(const char* pattern, HandlerWithContentReader handler); + Server& Delete(const char* pattern, Handler handler); + Server& Delete(const char* pattern, HandlerWithContentReader handler); + Server& Options(const char* pattern, Handler handler); - bool set_base_dir(const char *dir, const char *mount_point = nullptr); - bool set_mount_point(const char *mount_point, const char *dir); - bool remove_mount_point(const char *mount_point); - void set_file_extension_and_mimetype_mapping(const char *ext, - const char *mime); - void set_file_request_handler(Handler handler); + bool set_base_dir(const char* dir, const char* mount_point = nullptr); + bool set_mount_point(const char* mount_point, const char* dir); + bool remove_mount_point(const char* mount_point); + void set_file_extension_and_mimetype_mapping(const char* ext, + const char* mime); + void set_file_request_handler(Handler handler); - void set_error_handler(Handler handler); - void set_expect_100_continue_handler(Expect100ContinueHandler handler); - void set_logger(Logger logger); + void set_error_handler(Handler handler); + void set_expect_100_continue_handler(Expect100ContinueHandler handler); + void set_logger(Logger logger); - void set_tcp_nodelay(bool on); - void set_socket_options(SocketOptions socket_options); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); - void set_keep_alive_max_count(size_t count); - void set_keep_alive_timeout(time_t sec); - void set_read_timeout(time_t sec, time_t usec = 0); - void set_write_timeout(time_t sec, time_t usec = 0); - void set_idle_interval(time_t sec, time_t usec = 0); + void set_keep_alive_max_count(size_t count); + void set_keep_alive_timeout(time_t sec); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + void set_idle_interval(time_t sec, time_t usec = 0); - void set_payload_max_length(size_t length); + void set_payload_max_length(size_t length); - bool bind_to_port(const char *host, int port, int socket_flags = 0); - int bind_to_any_port(const char *host, int socket_flags = 0); - bool listen_after_bind(); + bool bind_to_port(const char* host, int port, int socket_flags = 0); + int bind_to_any_port(const char* host, int socket_flags = 0); + bool listen_after_bind(); - bool listen(const char *host, int port, int socket_flags = 0); + bool listen(const char* host, int port, int socket_flags = 0); - bool is_running() const; - void stop(); + bool is_running() const; + void stop(); - std::function new_task_queue; + std::function new_task_queue; protected: - bool process_request(Stream &strm, bool close_connection, - bool &connection_closed, - const std::function &setup_request); - - std::atomic svr_sock_; - size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; - time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; - time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; - time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; - time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; - time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; - time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; - time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; - size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; + bool process_request(Stream& strm, bool close_connection, + bool& connection_closed, + const std::function& setup_request); + + std::atomic svr_sock_; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; private: - using Handlers = std::vector>; - using HandlersForContentReader = - std::vector>; - - socket_t create_server_socket(const char *host, int port, int socket_flags, - SocketOptions socket_options) const; - int bind_internal(const char *host, int port, int socket_flags); - bool listen_internal(); - - bool routing(Request &req, Response &res, Stream &strm); - bool handle_file_request(Request &req, Response &res, bool head = false); - bool dispatch_request(Request &req, Response &res, const Handlers &handlers); - bool - dispatch_request_for_content_reader(Request &req, Response &res, - ContentReader content_reader, - const HandlersForContentReader &handlers); - - bool parse_request_line(const char *s, Request &req); - bool write_response(Stream &strm, bool close_connection, const Request &req, - Response &res); - bool write_content_with_provider(Stream &strm, const Request &req, - Response &res, const std::string &boundary, - const std::string &content_type); - bool read_content(Stream &strm, Request &req, Response &res); - bool - read_content_with_content_receiver(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver); - bool read_content_core(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader mulitpart_header, - ContentReceiver multipart_receiver); - - virtual bool process_and_close_socket(socket_t sock); - - std::atomic is_running_; - std::vector> base_dirs_; - std::map file_extension_and_mimetype_map_; - Handler file_request_handler_; - Handlers get_handlers_; - Handlers post_handlers_; - HandlersForContentReader post_handlers_for_content_reader_; - Handlers put_handlers_; - HandlersForContentReader put_handlers_for_content_reader_; - Handlers patch_handlers_; - HandlersForContentReader patch_handlers_for_content_reader_; - Handlers delete_handlers_; - HandlersForContentReader delete_handlers_for_content_reader_; - Handlers options_handlers_; - Handler error_handler_; - Logger logger_; - Expect100ContinueHandler expect_100_continue_handler_; - - bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; - SocketOptions socket_options_ = default_socket_options; + using Handlers = std::vector>; + using HandlersForContentReader = std::vector>; + + socket_t create_server_socket(const char* host, int port, int socket_flags, + SocketOptions socket_options) const; + int bind_internal(const char* host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request& req, Response& res, Stream& strm); + bool handle_file_request(Request& req, Response& res, bool head = false); + bool dispatch_request(Request& req, Response& res, const Handlers& handlers); + bool + dispatch_request_for_content_reader(Request& req, Response& res, + ContentReader content_reader, + const HandlersForContentReader& handlers); + + bool parse_request_line(const char* s, Request& req); + bool write_response(Stream& strm, bool close_connection, const Request& req, + Response& res); + bool write_content_with_provider(Stream& strm, const Request& req, + Response& res, const std::string& boundary, + const std::string& content_type); + bool read_content(Stream& strm, Request& req, Response& res); + bool + read_content_with_content_receiver(Stream& strm, Request& req, Response& res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream& strm, Request& req, Response& res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver); + + virtual bool process_and_close_socket(socket_t sock); + + std::atomic is_running_; + std::vector> base_dirs_; + std::map file_extension_and_mimetype_map_; + Handler file_request_handler_; + Handlers get_handlers_; + Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; + Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; + Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; + Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; + Handlers options_handlers_; + Handler error_handler_; + Logger logger_; + Expect100ContinueHandler expect_100_continue_handler_; + + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = default_socket_options; }; enum Error { - Success = 0, - Unknown, - Connection, - BindIPAddress, - Read, - Write, - ExceedRedirectCount, - Canceled, - SSLConnection, - SSLLoadingCerts, - SSLServerVerification + Success = 0, + Unknown, + Connection, + BindIPAddress, + Read, + Write, + ExceedRedirectCount, + Canceled, + SSLConnection, + SSLLoadingCerts, + SSLServerVerification }; class Result { public: - Result(const std::shared_ptr &res, Error err) - : res_(res), err_(err) {} - operator bool() const { return res_ != nullptr; } - bool operator==(std::nullptr_t) const { return res_ == nullptr; } - bool operator!=(std::nullptr_t) const { return res_ != nullptr; } - const Response &value() const { return *res_; } - const Response &operator*() const { return *res_; } - const Response *operator->() const { return res_.get(); } - Error error() const { return err_; } + Result(const std::shared_ptr& res, Error err) + : res_(res) + , err_(err) + { + } + operator bool() const { return res_ != nullptr; } + bool operator==(std::nullptr_t) const { return res_ == nullptr; } + bool operator!=(std::nullptr_t) const { return res_ != nullptr; } + const Response& value() const { return *res_; } + const Response& operator*() const { return *res_; } + const Response* operator->() const { return res_.get(); } + Error error() const { return err_; } private: - std::shared_ptr res_; - Error err_; + std::shared_ptr res_; + Error err_; }; class ClientImpl { public: - explicit ClientImpl(const std::string &host); - - explicit ClientImpl(const std::string &host, int port); - - explicit ClientImpl(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); - - virtual ~ClientImpl(); - - virtual bool is_valid() const; - - Result Get(const char *path); - Result Get(const char *path, const Headers &headers); - Result Get(const char *path, Progress progress); - Result Get(const char *path, const Headers &headers, Progress progress); - Result Get(const char *path, ContentReceiver content_receiver); - Result Get(const char *path, const Headers &headers, - ContentReceiver content_receiver); - Result Get(const char *path, ContentReceiver content_receiver, - Progress progress); - Result Get(const char *path, const Headers &headers, - ContentReceiver content_receiver, Progress progress); - Result Get(const char *path, ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const char *path, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress); - Result Get(const char *path, const Headers &headers, - ResponseHandler response_handler, ContentReceiver content_receiver, - Progress progress); - - Result Head(const char *path); - Result Head(const char *path, const Headers &headers); - - Result Post(const char *path); - Result Post(const char *path, const std::string &body, - const char *content_type); - Result Post(const char *path, const Headers &headers, const std::string &body, - const char *content_type); - Result Post(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Post(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Post(const char *path, const Params ¶ms); - Result Post(const char *path, const Headers &headers, const Params ¶ms); - Result Post(const char *path, const MultipartFormDataItems &items); - Result Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items); - - Result Put(const char *path); - Result Put(const char *path, const std::string &body, - const char *content_type); - Result Put(const char *path, const Headers &headers, const std::string &body, - const char *content_type); - Result Put(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Put(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Put(const char *path, const Params ¶ms); - Result Put(const char *path, const Headers &headers, const Params ¶ms); - - Result Patch(const char *path, const std::string &body, - const char *content_type); - Result Patch(const char *path, const Headers &headers, - const std::string &body, const char *content_type); - Result Patch(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Patch(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type); - - Result Delete(const char *path); - Result Delete(const char *path, const std::string &body, - const char *content_type); - Result Delete(const char *path, const Headers &headers); - Result Delete(const char *path, const Headers &headers, - const std::string &body, const char *content_type); - - Result Options(const char *path); - Result Options(const char *path, const Headers &headers); - - bool send(const Request &req, Response &res); - - size_t is_socket_open() const; - - void stop(); - - void set_default_headers(Headers headers); - - void set_tcp_nodelay(bool on); - void set_socket_options(SocketOptions socket_options); - - void set_connection_timeout(time_t sec, time_t usec = 0); - void set_read_timeout(time_t sec, time_t usec = 0); - void set_write_timeout(time_t sec, time_t usec = 0); - - void set_basic_auth(const char *username, const char *password); - void set_bearer_token_auth(const char *token); + explicit ClientImpl(const std::string& host); + + explicit ClientImpl(const std::string& host, int port); + + explicit ClientImpl(const std::string& host, int port, + const std::string& client_cert_path, + const std::string& client_key_path); + + virtual ~ClientImpl(); + + virtual bool is_valid() const; + + Result Get(const char* path); + Result Get(const char* path, const Headers& headers); + Result Get(const char* path, Progress progress); + Result Get(const char* path, const Headers& headers, Progress progress); + Result Get(const char* path, ContentReceiver content_receiver); + Result Get(const char* path, const Headers& headers, + ContentReceiver content_receiver); + Result Get(const char* path, ContentReceiver content_receiver, + Progress progress); + Result Get(const char* path, const Headers& headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const char* path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char* path, const Headers& headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char* path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + Result Get(const char* path, const Headers& headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + + Result Head(const char* path); + Result Head(const char* path, const Headers& headers); + + Result Post(const char* path); + Result Post(const char* path, const std::string& body, + const char* content_type); + Result Post(const char* path, const Headers& headers, const std::string& body, + const char* content_type); + Result Post(const char* path, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Post(const char* path, const Headers& headers, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Post(const char* path, const Params& params); + Result Post(const char* path, const Headers& headers, const Params& params); + Result Post(const char* path, const MultipartFormDataItems& items); + Result Post(const char* path, const Headers& headers, + const MultipartFormDataItems& items); + + Result Put(const char* path); + Result Put(const char* path, const std::string& body, + const char* content_type); + Result Put(const char* path, const Headers& headers, const std::string& body, + const char* content_type); + Result Put(const char* path, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Put(const char* path, const Headers& headers, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Put(const char* path, const Params& params); + Result Put(const char* path, const Headers& headers, const Params& params); + + Result Patch(const char* path, const std::string& body, + const char* content_type); + Result Patch(const char* path, const Headers& headers, + const std::string& body, const char* content_type); + Result Patch(const char* path, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Patch(const char* path, const Headers& headers, size_t content_length, + ContentProvider content_provider, const char* content_type); + + Result Delete(const char* path); + Result Delete(const char* path, const std::string& body, + const char* content_type); + Result Delete(const char* path, const Headers& headers); + Result Delete(const char* path, const Headers& headers, + const std::string& body, const char* content_type); + + Result Options(const char* path); + Result Options(const char* path, const Headers& headers); + + bool send(const Request& req, Response& res); + + size_t is_socket_open() const; + + void stop(); + + void set_default_headers(Headers headers); + + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + + void set_basic_auth(const char* username, const char* password); + void set_bearer_token_auth(const char* token); #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_digest_auth(const char *username, const char *password); + void set_digest_auth(const char* username, const char* password); #endif - void set_keep_alive(bool on); - void set_follow_location(bool on); + void set_keep_alive(bool on); + void set_follow_location(bool on); - void set_compress(bool on); + void set_compress(bool on); - void set_decompress(bool on); + void set_decompress(bool on); - void set_interface(const char *intf); + void set_interface(const char* intf); - void set_proxy(const char *host, int port); - void set_proxy_basic_auth(const char *username, const char *password); - void set_proxy_bearer_token_auth(const char *token); + void set_proxy(const char* host, int port); + void set_proxy_basic_auth(const char* username, const char* password); + void set_proxy_bearer_token_auth(const char* token); #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_proxy_digest_auth(const char *username, const char *password); + void set_proxy_digest_auth(const char* username, const char* password); #endif - void set_logger(Logger logger); + void set_logger(Logger logger); protected: - struct Socket { - socket_t sock = INVALID_SOCKET; + struct Socket { + socket_t sock = INVALID_SOCKET; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSL *ssl = nullptr; + SSL* ssl = nullptr; #endif - bool is_open() const { return sock != INVALID_SOCKET; } - }; + bool is_open() const { return sock != INVALID_SOCKET; } + }; - virtual bool create_and_connect_socket(Socket &socket); - virtual void close_socket(Socket &socket, bool process_socket_ret); + virtual bool create_and_connect_socket(Socket& socket); + virtual void close_socket(Socket& socket, bool process_socket_ret); - bool process_request(Stream &strm, const Request &req, Response &res, - bool close_connection); + bool process_request(Stream& strm, const Request& req, Response& res, + bool close_connection); - Error get_last_error() const; + Error get_last_error() const; - // Error state - mutable Error error_ = Error::Success; + // Error state + mutable Error error_ = Error::Success; - // Socket endoint information - const std::string host_; - const int port_; - const std::string host_and_port_; + // Socket endoint information + const std::string host_; + const int port_; + const std::string host_and_port_; - // Current open socket - Socket socket_; - mutable std::mutex socket_mutex_; - std::recursive_mutex request_mutex_; + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; - // Default headers - Headers default_headers_; + // Default headers + Headers default_headers_; - // Settings - std::string client_cert_path_; - std::string client_key_path_; + // Settings + std::string client_cert_path_; + std::string client_key_path_; - time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; - time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; - time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; - time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; - time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; - time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; - std::string basic_auth_username_; - std::string basic_auth_password_; - std::string bearer_token_auth_token_; + std::string basic_auth_username_; + std::string basic_auth_password_; + std::string bearer_token_auth_token_; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - std::string digest_auth_username_; - std::string digest_auth_password_; + std::string digest_auth_username_; + std::string digest_auth_password_; #endif - bool keep_alive_ = false; - bool follow_location_ = false; + bool keep_alive_ = false; + bool follow_location_ = false; - bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; - SocketOptions socket_options_ = nullptr; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = nullptr; - bool compress_ = false; - bool decompress_ = true; + bool compress_ = false; + bool decompress_ = true; - std::string interface_; + std::string interface_; - std::string proxy_host_; - int proxy_port_ = -1; + std::string proxy_host_; + int proxy_port_ = -1; - std::string proxy_basic_auth_username_; - std::string proxy_basic_auth_password_; - std::string proxy_bearer_token_auth_token_; + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; + std::string proxy_bearer_token_auth_token_; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - std::string proxy_digest_auth_username_; - std::string proxy_digest_auth_password_; + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; #endif - Logger logger_; - - void copy_settings(const ClientImpl &rhs) { - client_cert_path_ = rhs.client_cert_path_; - client_key_path_ = rhs.client_key_path_; - connection_timeout_sec_ = rhs.connection_timeout_sec_; - read_timeout_sec_ = rhs.read_timeout_sec_; - read_timeout_usec_ = rhs.read_timeout_usec_; - write_timeout_sec_ = rhs.write_timeout_sec_; - write_timeout_usec_ = rhs.write_timeout_usec_; - basic_auth_username_ = rhs.basic_auth_username_; - basic_auth_password_ = rhs.basic_auth_password_; - bearer_token_auth_token_ = rhs.bearer_token_auth_token_; + Logger logger_; + + void copy_settings(const ClientImpl& rhs) + { + client_cert_path_ = rhs.client_cert_path_; + client_key_path_ = rhs.client_key_path_; + connection_timeout_sec_ = rhs.connection_timeout_sec_; + read_timeout_sec_ = rhs.read_timeout_sec_; + read_timeout_usec_ = rhs.read_timeout_usec_; + write_timeout_sec_ = rhs.write_timeout_sec_; + write_timeout_usec_ = rhs.write_timeout_usec_; + basic_auth_username_ = rhs.basic_auth_username_; + basic_auth_password_ = rhs.basic_auth_password_; + bearer_token_auth_token_ = rhs.bearer_token_auth_token_; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - digest_auth_username_ = rhs.digest_auth_username_; - digest_auth_password_ = rhs.digest_auth_password_; + digest_auth_username_ = rhs.digest_auth_username_; + digest_auth_password_ = rhs.digest_auth_password_; #endif - keep_alive_ = rhs.keep_alive_; - follow_location_ = rhs.follow_location_; - tcp_nodelay_ = rhs.tcp_nodelay_; - socket_options_ = rhs.socket_options_; - compress_ = rhs.compress_; - decompress_ = rhs.decompress_; - interface_ = rhs.interface_; - proxy_host_ = rhs.proxy_host_; - proxy_port_ = rhs.proxy_port_; - proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; - proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; - proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_; + keep_alive_ = rhs.keep_alive_; + follow_location_ = rhs.follow_location_; + tcp_nodelay_ = rhs.tcp_nodelay_; + socket_options_ = rhs.socket_options_; + compress_ = rhs.compress_; + decompress_ = rhs.decompress_; + interface_ = rhs.interface_; + proxy_host_ = rhs.proxy_host_; + proxy_port_ = rhs.proxy_port_; + proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; + proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; + proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; - proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; + proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; + proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; #endif - logger_ = rhs.logger_; - } + logger_ = rhs.logger_; + } private: - socket_t create_client_socket() const; - bool read_response_line(Stream &strm, Response &res); - bool write_request(Stream &strm, const Request &req, bool close_connection); - bool redirect(const Request &req, Response &res); - bool handle_request(Stream &strm, const Request &req, Response &res, - bool close_connection); - void stop_core(); - std::shared_ptr send_with_content_provider( - const char *method, const char *path, const Headers &headers, - const std::string &body, size_t content_length, - ContentProvider content_provider, const char *content_type); - - virtual bool process_socket(Socket &socket, - std::function callback); - virtual bool is_ssl() const; + socket_t create_client_socket() const; + bool read_response_line(Stream& strm, Response& res); + bool write_request(Stream& strm, const Request& req, bool close_connection); + bool redirect(const Request& req, Response& res); + bool handle_request(Stream& strm, const Request& req, Response& res, + bool close_connection); + void stop_core(); + std::shared_ptr send_with_content_provider( + const char* method, const char* path, const Headers& headers, + const std::string& body, size_t content_length, + ContentProvider content_provider, const char* content_type); + + virtual bool process_socket(Socket& socket, + std::function callback); + virtual bool is_ssl() const; }; class Client { public: - // Universal interface - explicit Client(const char *scheme_host_port); - - explicit Client(const char *scheme_host_port, - const std::string &client_cert_path, - const std::string &client_key_path); - - // HTTP only interface - explicit Client(const std::string &host, int port); - - explicit Client(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); - - ~Client(); - - bool is_valid() const; - - Result Get(const char *path); - Result Get(const char *path, const Headers &headers); - Result Get(const char *path, Progress progress); - Result Get(const char *path, const Headers &headers, Progress progress); - Result Get(const char *path, ContentReceiver content_receiver); - Result Get(const char *path, const Headers &headers, - ContentReceiver content_receiver); - Result Get(const char *path, ContentReceiver content_receiver, - Progress progress); - Result Get(const char *path, const Headers &headers, - ContentReceiver content_receiver, Progress progress); - Result Get(const char *path, ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver); - Result Get(const char *path, const Headers &headers, - ResponseHandler response_handler, ContentReceiver content_receiver, - Progress progress); - Result Get(const char *path, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress); - - Result Head(const char *path); - Result Head(const char *path, const Headers &headers); - - Result Post(const char *path); - Result Post(const char *path, const std::string &body, - const char *content_type); - Result Post(const char *path, const Headers &headers, const std::string &body, - const char *content_type); - Result Post(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Post(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Post(const char *path, const Params ¶ms); - Result Post(const char *path, const Headers &headers, const Params ¶ms); - Result Post(const char *path, const MultipartFormDataItems &items); - Result Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items); - Result Put(const char *path); - Result Put(const char *path, const std::string &body, - const char *content_type); - Result Put(const char *path, const Headers &headers, const std::string &body, - const char *content_type); - Result Put(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Put(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Put(const char *path, const Params ¶ms); - Result Put(const char *path, const Headers &headers, const Params ¶ms); - Result Patch(const char *path, const std::string &body, - const char *content_type); - Result Patch(const char *path, const Headers &headers, - const std::string &body, const char *content_type); - Result Patch(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type); - Result Patch(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type); - - Result Delete(const char *path); - Result Delete(const char *path, const std::string &body, - const char *content_type); - Result Delete(const char *path, const Headers &headers); - Result Delete(const char *path, const Headers &headers, - const std::string &body, const char *content_type); - - Result Options(const char *path); - Result Options(const char *path, const Headers &headers); - - bool send(const Request &req, Response &res); - - size_t is_socket_open() const; - - void stop(); - - void set_default_headers(Headers headers); - - void set_tcp_nodelay(bool on); - void set_socket_options(SocketOptions socket_options); - - void set_connection_timeout(time_t sec, time_t usec = 0); - void set_read_timeout(time_t sec, time_t usec = 0); - void set_write_timeout(time_t sec, time_t usec = 0); - - void set_basic_auth(const char *username, const char *password); - void set_bearer_token_auth(const char *token); + // Universal interface + explicit Client(const char* scheme_host_port); + + explicit Client(const char* scheme_host_port, + const std::string& client_cert_path, + const std::string& client_key_path); + + // HTTP only interface + explicit Client(const std::string& host, int port); + + explicit Client(const std::string& host, int port, + const std::string& client_cert_path, + const std::string& client_key_path); + + ~Client(); + + bool is_valid() const; + + Result Get(const char* path); + Result Get(const char* path, const Headers& headers); + Result Get(const char* path, Progress progress); + Result Get(const char* path, const Headers& headers, Progress progress); + Result Get(const char* path, ContentReceiver content_receiver); + Result Get(const char* path, const Headers& headers, + ContentReceiver content_receiver); + Result Get(const char* path, ContentReceiver content_receiver, + Progress progress); + Result Get(const char* path, const Headers& headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const char* path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char* path, const Headers& headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char* path, const Headers& headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + Result Get(const char* path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + + Result Head(const char* path); + Result Head(const char* path, const Headers& headers); + + Result Post(const char* path); + Result Post(const char* path, const std::string& body, + const char* content_type); + Result Post(const char* path, const Headers& headers, const std::string& body, + const char* content_type); + Result Post(const char* path, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Post(const char* path, const Headers& headers, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Post(const char* path, const Params& params); + Result Post(const char* path, const Headers& headers, const Params& params); + Result Post(const char* path, const MultipartFormDataItems& items); + Result Post(const char* path, const Headers& headers, + const MultipartFormDataItems& items); + Result Put(const char* path); + Result Put(const char* path, const std::string& body, + const char* content_type); + Result Put(const char* path, const Headers& headers, const std::string& body, + const char* content_type); + Result Put(const char* path, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Put(const char* path, const Headers& headers, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Put(const char* path, const Params& params); + Result Put(const char* path, const Headers& headers, const Params& params); + Result Patch(const char* path, const std::string& body, + const char* content_type); + Result Patch(const char* path, const Headers& headers, + const std::string& body, const char* content_type); + Result Patch(const char* path, size_t content_length, + ContentProvider content_provider, const char* content_type); + Result Patch(const char* path, const Headers& headers, size_t content_length, + ContentProvider content_provider, const char* content_type); + + Result Delete(const char* path); + Result Delete(const char* path, const std::string& body, + const char* content_type); + Result Delete(const char* path, const Headers& headers); + Result Delete(const char* path, const Headers& headers, + const std::string& body, const char* content_type); + + Result Options(const char* path); + Result Options(const char* path, const Headers& headers); + + bool send(const Request& req, Response& res); + + size_t is_socket_open() const; + + void stop(); + + void set_default_headers(Headers headers); + + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + + void set_basic_auth(const char* username, const char* password); + void set_bearer_token_auth(const char* token); #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_digest_auth(const char *username, const char *password); + void set_digest_auth(const char* username, const char* password); #endif - void set_keep_alive(bool on); - void set_follow_location(bool on); + void set_keep_alive(bool on); + void set_follow_location(bool on); - void set_compress(bool on); + void set_compress(bool on); - void set_decompress(bool on); + void set_decompress(bool on); - void set_interface(const char *intf); + void set_interface(const char* intf); - void set_proxy(const char *host, int port); - void set_proxy_basic_auth(const char *username, const char *password); - void set_proxy_bearer_token_auth(const char *token); + void set_proxy(const char* host, int port); + void set_proxy_basic_auth(const char* username, const char* password); + void set_proxy_bearer_token_auth(const char* token); #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - void set_proxy_digest_auth(const char *username, const char *password); + void set_proxy_digest_auth(const char* username, const char* password); #endif - void set_logger(Logger logger); + void set_logger(Logger logger); - // SSL + // SSL #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - Client &set_ca_cert_path(const char *ca_cert_file_path, - const char *ca_cert_dir_path = nullptr); + Client& set_ca_cert_path(const char* ca_cert_file_path, + const char* ca_cert_dir_path = nullptr); - Client &set_ca_cert_store(X509_STORE *ca_cert_store); + Client& set_ca_cert_store(X509_STORE* ca_cert_store); - Client &enable_server_certificate_verification(bool enabled); + Client& enable_server_certificate_verification(bool enabled); - long get_openssl_verify_result() const; + long get_openssl_verify_result() const; - SSL_CTX *ssl_context() const; + SSL_CTX* ssl_context() const; #endif private: - std::shared_ptr cli_; + std::shared_ptr cli_; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - bool is_ssl_ = false; + bool is_ssl_ = false; #endif }; // namespace httplib #ifdef CPPHTTPLIB_OPENSSL_SUPPORT class SSLServer : public Server { public: - SSLServer(const char *cert_path, const char *private_key_path, - const char *client_ca_cert_file_path = nullptr, - const char *client_ca_cert_dir_path = nullptr); + SSLServer(const char* cert_path, const char* private_key_path, + const char* client_ca_cert_file_path = nullptr, + const char* client_ca_cert_dir_path = nullptr); - SSLServer(X509 *cert, EVP_PKEY *private_key, - X509_STORE *client_ca_cert_store = nullptr); + SSLServer(X509* cert, EVP_PKEY* private_key, + X509_STORE* client_ca_cert_store = nullptr); - ~SSLServer() override; + ~SSLServer() override; - bool is_valid() const override; + bool is_valid() const override; private: - bool process_and_close_socket(socket_t sock) override; + bool process_and_close_socket(socket_t sock) override; - SSL_CTX *ctx_; - std::mutex ctx_mutex_; + SSL_CTX* ctx_; + std::mutex ctx_mutex_; }; class SSLClient : public ClientImpl { public: - explicit SSLClient(const std::string &host); + explicit SSLClient(const std::string& host); - explicit SSLClient(const std::string &host, int port); + explicit SSLClient(const std::string& host, int port); - explicit SSLClient(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path); + explicit SSLClient(const std::string& host, int port, + const std::string& client_cert_path, + const std::string& client_key_path); - explicit SSLClient(const std::string &host, int port, X509 *client_cert, - EVP_PKEY *client_key); + explicit SSLClient(const std::string& host, int port, X509* client_cert, + EVP_PKEY* client_key); - ~SSLClient() override; + ~SSLClient() override; - bool is_valid() const override; + bool is_valid() const override; - void set_ca_cert_path(const char *ca_cert_file_path, - const char *ca_cert_dir_path = nullptr); + void set_ca_cert_path(const char* ca_cert_file_path, + const char* ca_cert_dir_path = nullptr); - void set_ca_cert_store(X509_STORE *ca_cert_store); + void set_ca_cert_store(X509_STORE* ca_cert_store); - void enable_server_certificate_verification(bool enabled); + void enable_server_certificate_verification(bool enabled); - long get_openssl_verify_result() const; + long get_openssl_verify_result() const; - SSL_CTX *ssl_context() const; + SSL_CTX* ssl_context() const; private: - bool create_and_connect_socket(Socket &socket) override; - void close_socket(Socket &socket, bool process_socket_ret) override; + bool create_and_connect_socket(Socket& socket) override; + void close_socket(Socket& socket, bool process_socket_ret) override; - bool process_socket(Socket &socket, - std::function callback) override; - bool is_ssl() const override; + bool process_socket(Socket& socket, + std::function callback) override; + bool is_ssl() const override; - bool connect_with_proxy(Socket &sock, Response &res, bool &success); - bool initialize_ssl(Socket &socket); + bool connect_with_proxy(Socket& sock, Response& res, bool& success); + bool initialize_ssl(Socket& socket); - bool load_certs(); + bool load_certs(); - bool verify_host(X509 *server_cert) const; - bool verify_host_with_subject_alt_name(X509 *server_cert) const; - bool verify_host_with_common_name(X509 *server_cert) const; - bool check_host_name(const char *pattern, size_t pattern_len) const; + bool verify_host(X509* server_cert) const; + bool verify_host_with_subject_alt_name(X509* server_cert) const; + bool verify_host_with_common_name(X509* server_cert) const; + bool check_host_name(const char* pattern, size_t pattern_len) const; - SSL_CTX *ctx_; - std::mutex ctx_mutex_; - std::once_flag initialize_cert_; + SSL_CTX* ctx_; + std::mutex ctx_mutex_; + std::once_flag initialize_cert_; - std::vector host_components_; + std::vector host_components_; - std::string ca_cert_file_path_; - std::string ca_cert_dir_path_; - X509_STORE *ca_cert_store_ = nullptr; - bool server_certificate_verification_ = true; - long verify_result_ = 0; + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + X509_STORE* ca_cert_store_ = nullptr; + bool server_certificate_verification_ = true; + long verify_result_ = 0; - friend class ClientImpl; + friend class ClientImpl; }; #endif @@ -1211,3404 +1233,3871 @@ class SSLClient : public ClientImpl { namespace detail { -inline bool is_hex(char c, int &v) { - if (0x20 <= c && isdigit(c)) { - v = c - '0'; - return true; - } else if ('A' <= c && c <= 'F') { - v = c - 'A' + 10; - return true; - } else if ('a' <= c && c <= 'f') { - v = c - 'a' + 10; - return true; - } - return false; -} + inline bool is_hex(char c, int& v) + { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; + } -inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, - int &val) { - if (i >= s.size()) { return false; } + inline bool from_hex_to_i(const std::string& s, size_t i, size_t cnt, + int& val) + { + if (i >= s.size()) { + return false; + } - val = 0; - for (; cnt; i++, cnt--) { - if (!s[i]) { return false; } - int v = 0; - if (is_hex(s[i], v)) { - val = val * 16 + v; - } else { - return false; - } - } - return true; -} - -inline std::string from_i_to_hex(size_t n) { - const char *charset = "0123456789abcdef"; - std::string ret; - do { - ret = charset[n & 15] + ret; - n >>= 4; - } while (n > 0); - return ret; -} - -inline bool start_with(const std::string &a, const std::string &b) { - if (a.size() < b.size()) { return false; } - for (size_t i = 0; i < b.size(); i++) { - if (std::tolower(a[i]) != std::tolower(b[i])) { return false; } - } - return true; -} - -inline size_t to_utf8(int code, char *buff) { - if (code < 0x0080) { - buff[0] = (code & 0x7F); - return 1; - } else if (code < 0x0800) { - buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); - buff[1] = static_cast(0x80 | (code & 0x3F)); - return 2; - } else if (code < 0xD800) { - buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); - buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); - buff[2] = static_cast(0x80 | (code & 0x3F)); - return 3; - } else if (code < 0xE000) { // D800 - DFFF is invalid... - return 0; - } else if (code < 0x10000) { - buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); - buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); - buff[2] = static_cast(0x80 | (code & 0x3F)); - return 3; - } else if (code < 0x110000) { - buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); - buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); - buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); - buff[3] = static_cast(0x80 | (code & 0x3F)); - return 4; - } - - // NOTREACHED - return 0; -} - -// NOTE: This code came up with the following stackoverflow post: -// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c -inline std::string base64_encode(const std::string &in) { - static const auto lookup = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - std::string out; - out.reserve(in.size()); - - int val = 0; - int valb = -6; - - for (auto c : in) { - val = (val << 8) + static_cast(c); - valb += 8; - while (valb >= 0) { - out.push_back(lookup[(val >> valb) & 0x3F]); - valb -= 6; - } - } - - if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); } - - while (out.size() % 4) { - out.push_back('='); - } - - return out; -} - -inline bool is_file(const std::string &path) { - struct stat st; - return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); -} - -inline bool is_dir(const std::string &path) { - struct stat st; - return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); -} - -inline bool is_valid_path(const std::string &path) { - size_t level = 0; - size_t i = 0; - - // Skip slash - while (i < path.size() && path[i] == '/') { - i++; - } - - while (i < path.size()) { - // Read component - auto beg = i; - while (i < path.size() && path[i] != '/') { - i++; - } - - auto len = i - beg; - assert(len > 0); - - if (!path.compare(beg, len, ".")) { - ; - } else if (!path.compare(beg, len, "..")) { - if (level == 0) { return false; } - level--; - } else { - level++; + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { + return false; + } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; } - // Skip slash - while (i < path.size() && path[i] == '/') { - i++; + inline std::string from_i_to_hex(size_t n) + { + const char* charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); + return ret; } - } - return true; -} + inline bool start_with(const std::string& a, const std::string& b) + { + if (a.size() < b.size()) { + return false; + } + for (size_t i = 0; i < b.size(); i++) { + if (std::tolower(a[i]) != std::tolower(b[i])) { + return false; + } + } + return true; + } -inline std::string encode_url(const std::string &s) { - std::string result; + inline size_t to_utf8(int code, char* buff) + { + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (code & 0x3F)); + return 4; + } - for (size_t i = 0; s[i]; i++) { - switch (s[i]) { - case ' ': result += "%20"; break; - case '+': result += "%2B"; break; - case '\r': result += "%0D"; break; - case '\n': result += "%0A"; break; - case '\'': result += "%27"; break; - case ',': result += "%2C"; break; - // case ':': result += "%3A"; break; // ok? probably... - case ';': result += "%3B"; break; - default: - auto c = static_cast(s[i]); - if (c >= 0x80) { - result += '%'; - char hex[4]; - auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); - assert(len == 2); - result.append(hex, static_cast(len)); - } else { - result += s[i]; - } - break; + // NOTREACHED + return 0; } - } - return result; -} + // NOTE: This code came up with the following stackoverflow post: + // https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c + inline std::string base64_encode(const std::string& in) + { + static const auto lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -inline std::string decode_url(const std::string &s, - bool convert_plus_to_space) { - std::string result; + std::string out; + out.reserve(in.size()); - for (size_t i = 0; i < s.size(); i++) { - if (s[i] == '%' && i + 1 < s.size()) { - if (s[i + 1] == 'u') { int val = 0; - if (from_hex_to_i(s, i + 2, 4, val)) { - // 4 digits Unicode codes - char buff[4]; - size_t len = to_utf8(val, buff); - if (len > 0) { result.append(buff, len); } - i += 5; // 'u0000' - } else { - result += s[i]; + int valb = -6; + + for (auto c : in) { + val = (val << 8) + static_cast(c); + valb += 8; + while (valb >= 0) { + out.push_back(lookup[(val >> valb) & 0x3F]); + valb -= 6; + } } - } else { - int val = 0; - if (from_hex_to_i(s, i + 1, 2, val)) { - // 2 digits hex codes - result += static_cast(val); - i += 2; // '00' - } else { - result += s[i]; + + if (valb > -6) { + out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); } - } - } else if (convert_plus_to_space && s[i] == '+') { - result += ' '; - } else { - result += s[i]; + + while (out.size() % 4) { + out.push_back('='); + } + + return out; } - } - return result; -} + inline bool is_file(const std::string& path) + { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); + } -inline void read_file(const std::string &path, std::string &out) { - std::ifstream fs(path, std::ios_base::binary); - fs.seekg(0, std::ios_base::end); - auto size = fs.tellg(); - fs.seekg(0); - out.resize(static_cast(size)); - fs.read(&out[0], static_cast(size)); -} + inline bool is_dir(const std::string& path) + { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); + } -inline std::string file_extension(const std::string &path) { - std::smatch m; - static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); - if (std::regex_search(path, m, re)) { return m[1].str(); } - return std::string(); -} + inline bool is_valid_path(const std::string& path) + { + size_t level = 0; + size_t i = 0; -inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } -inline std::pair trim(const char *b, const char *e, size_t left, - size_t right) { - while (b + left < e && is_space_or_tab(b[left])) { - left++; - } - while (right > 0 && is_space_or_tab(b[right - 1])) { - right--; - } - return std::make_pair(left, right); -} + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } -inline std::string trim_copy(const std::string &s) { - auto r = trim(s.data(), s.data() + s.size(), 0, s.size()); - return s.substr(r.first, r.second - r.first); -} + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { + return false; + } + level--; + } else { + level++; + } -template void split(const char *b, const char *e, char d, Fn fn) { - size_t i = 0; - size_t beg = 0; + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } - while (e ? (b + i < e) : (b[i] != '\0')) { - if (b[i] == d) { - auto r = trim(b, e, beg, i); - if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } - beg = i + 1; + return true; } - i++; - } - - if (i) { - auto r = trim(b, e, beg, i); - if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } - } -} -// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` -// to store data. The call can set memory on stack for performance. -class stream_line_reader { -public: - stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) - : strm_(strm), fixed_buffer_(fixed_buffer), - fixed_buffer_size_(fixed_buffer_size) {} + inline std::string encode_url(const std::string& s) + { + std::string result; + + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': + result += "%20"; + break; + case '+': + result += "%2B"; + break; + case '\r': + result += "%0D"; + break; + case '\n': + result += "%0A"; + break; + case '\'': + result += "%27"; + break; + case ',': + result += "%2C"; + break; + // case ':': result += "%3A"; break; // ok? probably... + case ';': + result += "%3B"; + break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, static_cast(len)); + } else { + result += s[i]; + } + break; + } + } - const char *ptr() const { - if (glowable_buffer_.empty()) { - return fixed_buffer_; - } else { - return glowable_buffer_.data(); + return result; } - } - size_t size() const { - if (glowable_buffer_.empty()) { - return fixed_buffer_used_size_; - } else { - return glowable_buffer_.size(); + inline std::string decode_url(const std::string& s, + bool convert_plus_to_space) + { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { + result.append(buff, len); + } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += static_cast(val); + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (convert_plus_to_space && s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; } - } - bool end_with_crlf() const { - auto end = ptr() + size(); - return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; - } + inline void read_file(const std::string& path, std::string& out) + { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], static_cast(size)); + } - bool getline() { - fixed_buffer_used_size_ = 0; - glowable_buffer_.clear(); + inline std::string file_extension(const std::string& path) + { + std::smatch m; + static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, re)) { + return m[1].str(); + } + return std::string(); + } - for (size_t i = 0;; i++) { - char byte; - auto n = strm_.read(&byte, 1); + inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } - if (n < 0) { - return false; - } else if (n == 0) { - if (i == 0) { - return false; - } else { - break; + inline std::pair trim(const char* b, const char* e, size_t left, + size_t right) + { + while (b + left < e && is_space_or_tab(b[left])) { + left++; + } + while (right > 0 && is_space_or_tab(b[right - 1])) { + right--; } - } + return std::make_pair(left, right); + } + + inline std::string trim_copy(const std::string& s) + { + auto r = trim(s.data(), s.data() + s.size(), 0, s.size()); + return s.substr(r.first, r.second - r.first); + } - append(byte); + template + void split(const char* b, const char* e, char d, Fn fn) + { + size_t i = 0; + size_t beg = 0; + + while (e ? (b + i < e) : (b[i] != '\0')) { + if (b[i] == d) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { + fn(&b[r.first], &b[r.second]); + } + beg = i + 1; + } + i++; + } - if (byte == '\n') { break; } + if (i) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { + fn(&b[r.first], &b[r.second]); + } + } } - return true; - } + // NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` + // to store data. The call can set memory on stack for performance. + class stream_line_reader { + public: + stream_line_reader(Stream& strm, char* fixed_buffer, size_t fixed_buffer_size) + : strm_(strm) + , fixed_buffer_(fixed_buffer) + , fixed_buffer_size_(fixed_buffer_size) + { + } -private: - void append(char c) { - if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { - fixed_buffer_[fixed_buffer_used_size_++] = c; - fixed_buffer_[fixed_buffer_used_size_] = '\0'; - } else { - if (glowable_buffer_.empty()) { - assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); - glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); - } - glowable_buffer_ += c; - } - } - - Stream &strm_; - char *fixed_buffer_; - const size_t fixed_buffer_size_; - size_t fixed_buffer_used_size_ = 0; - std::string glowable_buffer_; -}; + const char* ptr() const + { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + size_t size() const + { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } + } -inline int close_socket(socket_t sock) { + bool end_with_crlf() const + { + auto end = ptr() + size(); + return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; + } + + bool getline() + { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { + break; + } + } + + return true; + } + + private: + void append(char c) + { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream& strm_; + char* fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; + }; + + inline int close_socket(socket_t sock) + { #ifdef _WIN32 - return closesocket(sock); + return closesocket(sock); #else - return close(sock); + return close(sock); #endif -} + } -template inline ssize_t handle_EINTR(T fn) { - ssize_t res = false; - while (true) { - res = fn(); - if (res < 0 && errno == EINTR) { continue; } - break; - } - return res; -} + template + inline ssize_t handle_EINTR(T fn) + { + ssize_t res = false; + while (true) { + res = fn(); + if (res < 0 && errno == EINTR) { + continue; + } + break; + } + return res; + } -inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { + inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) + { #ifdef CPPHTTPLIB_USE_POLL - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLIN; + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN; - auto timeout = static_cast(sec * 1000 + usec / 1000); + auto timeout = static_cast(sec * 1000 + usec / 1000); - return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); #else - fd_set fds; - FD_ZERO(&fds); - FD_SET(sock, &fds); + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); - timeval tv; - tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); - return handle_EINTR([&]() { - return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); - }); + return handle_EINTR([&]() { + return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); + }); #endif -} + } -inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { + inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) + { #ifdef CPPHTTPLIB_USE_POLL - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLOUT; + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLOUT; - auto timeout = static_cast(sec * 1000 + usec / 1000); + auto timeout = static_cast(sec * 1000 + usec / 1000); - return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); #else - fd_set fds; - FD_ZERO(&fds); - FD_SET(sock, &fds); + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); - timeval tv; - tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); - return handle_EINTR([&]() { - return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); - }); + return handle_EINTR([&]() { + return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); + }); #endif -} + } -inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { + inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) + { #ifdef CPPHTTPLIB_USE_POLL - struct pollfd pfd_read; - pfd_read.fd = sock; - pfd_read.events = POLLIN | POLLOUT; - - auto timeout = static_cast(sec * 1000 + usec / 1000); - - auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); - - if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { - int error = 0; - socklen_t len = sizeof(error); - auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, - reinterpret_cast(&error), &len); - return res >= 0 && !error; - } - return false; + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN | POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + + if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { + int error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len); + return res >= 0 && !error; + } + return false; #else - fd_set fdsr; - FD_ZERO(&fdsr); - FD_SET(sock, &fdsr); - - auto fdsw = fdsr; - auto fdse = fdsr; - - timeval tv; - tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); - - auto ret = handle_EINTR([&]() { - return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); - }); - - if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { - int error = 0; - socklen_t len = sizeof(error); - return getsockopt(sock, SOL_SOCKET, SO_ERROR, - reinterpret_cast(&error), &len) >= 0 && - !error; - } - return false; -#endif -} + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); -class SocketStream : public Stream { -public: - SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, - time_t write_timeout_sec, time_t write_timeout_usec); - ~SocketStream() override; + auto fdsw = fdsr; + auto fdse = fdsr; - bool is_readable() const override; - bool is_writable() const override; - ssize_t read(char *ptr, size_t size) override; - ssize_t write(const char *ptr, size_t size) override; - void get_remote_ip_and_port(std::string &ip, int &port) const override; + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); -private: - socket_t sock_; - time_t read_timeout_sec_; - time_t read_timeout_usec_; - time_t write_timeout_sec_; - time_t write_timeout_usec_; -}; + auto ret = handle_EINTR([&]() { + return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); + }); -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -class SSLSocketStream : public Stream { -public: - SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, - time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec); - ~SSLSocketStream() override; + if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { + int error = 0; + socklen_t len = sizeof(error); + return getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len) + >= 0 + && !error; + } + return false; +#endif + } - bool is_readable() const override; - bool is_writable() const override; - ssize_t read(char *ptr, size_t size) override; - ssize_t write(const char *ptr, size_t size) override; - void get_remote_ip_and_port(std::string &ip, int &port) const override; + class SocketStream : public Stream { + public: + SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec); + ~SocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char* ptr, size_t size) override; + ssize_t write(const char* ptr, size_t size) override; + void get_remote_ip_and_port(std::string& ip, int& port) const override; + + private: + socket_t sock_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; + }; -private: - socket_t sock_; - SSL *ssl_; - time_t read_timeout_sec_; - time_t read_timeout_usec_; - time_t write_timeout_sec_; - time_t write_timeout_usec_; -}; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + class SSLSocketStream : public Stream { + public: + SSLSocketStream(socket_t sock, SSL* ssl, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec); + ~SSLSocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char* ptr, size_t size) override; + ssize_t write(const char* ptr, size_t size) override; + void get_remote_ip_and_port(std::string& ip, int& port) const override; + + private: + socket_t sock_; + SSL* ssl_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; + }; #endif -class BufferStream : public Stream { -public: - BufferStream() = default; - ~BufferStream() override = default; + class BufferStream : public Stream { + public: + BufferStream() = default; + ~BufferStream() override = default; - bool is_readable() const override; - bool is_writable() const override; - ssize_t read(char *ptr, size_t size) override; - ssize_t write(const char *ptr, size_t size) override; - void get_remote_ip_and_port(std::string &ip, int &port) const override; + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char* ptr, size_t size) override; + ssize_t write(const char* ptr, size_t size) override; + void get_remote_ip_and_port(std::string& ip, int& port) const override; - const std::string &get_buffer() const; + const std::string& get_buffer() const; -private: - std::string buffer; - size_t position = 0; -}; + private: + std::string buffer; + size_t position = 0; + }; -inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { - using namespace std::chrono; - auto start = steady_clock::now(); - while (true) { - auto val = select_read(sock, 0, 10000); - if (val < 0) { - return false; - } else if (val == 0) { - auto current = steady_clock::now(); - auto duration = duration_cast(current - start); - auto timeout = keep_alive_timeout_sec * 1000; - if (duration.count() > timeout) { return false; } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } else { - return true; + inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) + { + using namespace std::chrono; + auto start = steady_clock::now(); + while (true) { + auto val = select_read(sock, 0, 10000); + if (val < 0) { + return false; + } else if (val == 0) { + auto current = steady_clock::now(); + auto duration = duration_cast(current - start); + auto timeout = keep_alive_timeout_sec * 1000; + if (duration.count() > timeout) { + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + return true; + } + } } - } -} - -template -inline bool -process_server_socket_core(socket_t sock, size_t keep_alive_max_count, - time_t keep_alive_timeout_sec, T callback) { - assert(keep_alive_max_count > 0); - auto ret = false; - auto count = keep_alive_max_count; - while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) { - auto close_connection = count == 1; - auto connection_closed = false; - ret = callback(close_connection, connection_closed); - if (!ret || connection_closed) { break; } - count--; - } - return ret; -} -template -inline bool -process_server_socket(socket_t sock, size_t keep_alive_max_count, - time_t keep_alive_timeout_sec, time_t read_timeout_sec, - time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, T callback) { - return process_server_socket_core( - sock, keep_alive_max_count, keep_alive_timeout_sec, - [&](bool close_connection, bool &connection_closed) { - SocketStream strm(sock, read_timeout_sec, read_timeout_usec, - write_timeout_sec, write_timeout_usec); - return callback(strm, close_connection, connection_closed); - }); -} + template + inline bool + process_server_socket_core(socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, T callback) + { + assert(keep_alive_max_count > 0); + auto ret = false; + auto count = keep_alive_max_count; + while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) { + auto close_connection = count == 1; + auto connection_closed = false; + ret = callback(close_connection, connection_closed); + if (!ret || connection_closed) { + break; + } + count--; + } + return ret; + } -template -inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, - time_t read_timeout_usec, - time_t write_timeout_sec, - time_t write_timeout_usec, T callback) { - SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + template + inline bool + process_server_socket(socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) + { + return process_server_socket_core( + sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool& connection_closed) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); - return callback(strm); -} + return callback(strm, close_connection, connection_closed); + }); + } + + template + inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, T callback) + { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); + } -inline int shutdown_socket(socket_t sock) { + inline int shutdown_socket(socket_t sock) + { #ifdef _WIN32 - return shutdown(sock, SD_BOTH); + return shutdown(sock, SD_BOTH); #else - return shutdown(sock, SHUT_RDWR); + return shutdown(sock, SHUT_RDWR); #endif -} + } -template -socket_t create_socket(const char *host, int port, int socket_flags, - bool tcp_nodelay, SocketOptions socket_options, - BindOrConnect bind_or_connect) { - // Get address info - struct addrinfo hints; - struct addrinfo *result; + template + socket_t create_socket(const char* host, int port, int socket_flags, + bool tcp_nodelay, SocketOptions socket_options, + BindOrConnect bind_or_connect) + { + // Get address info + struct addrinfo hints; + struct addrinfo* result; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = socket_flags; - hints.ai_protocol = 0; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; - auto service = std::to_string(port); + auto service = std::to_string(port); - if (getaddrinfo(host, service.c_str(), &hints, &result)) { + if (getaddrinfo(host, service.c_str(), &hints, &result)) { #ifdef __linux__ - res_init(); + res_init(); #endif - return INVALID_SOCKET; - } + return INVALID_SOCKET; + } - for (auto rp = result; rp; rp = rp->ai_next) { - // Create a socket + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket #ifdef _WIN32 - auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, - nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); - /** - * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 - * and above the socket creation fails on older Windows Systems. - * - * Let's try to create a socket the old way in this case. - * - * Reference: - * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa - * - * WSA_FLAG_NO_HANDLE_INHERIT: - * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with - * SP1, and later - * - */ - if (sock == INVALID_SOCKET) { - sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - } + auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, + nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); + /** + * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 + * and above the socket creation fails on older Windows Systems. + * + * Let's try to create a socket the old way in this case. + * + * Reference: + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa + * + * WSA_FLAG_NO_HANDLE_INHERIT: + * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with + * SP1, and later + * + */ + if (sock == INVALID_SOCKET) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + } #else - auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); #endif - if (sock == INVALID_SOCKET) { continue; } + if (sock == INVALID_SOCKET) { + continue; + } #ifdef __linux__ - if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { + continue; + } #endif - if (tcp_nodelay) { - int yes = 1; - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), - sizeof(yes)); - } + if (tcp_nodelay) { + int yes = 1; + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), + sizeof(yes)); + } - if (socket_options) { socket_options(sock); } + if (socket_options) { + socket_options(sock); + } - if (rp->ai_family == AF_INET6) { - int no = 0; - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&no), - sizeof(no)); - } + if (rp->ai_family == AF_INET6) { + int no = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&no), + sizeof(no)); + } - // bind or connect - if (bind_or_connect(sock, *rp)) { - freeaddrinfo(result); - return sock; - } + // bind or connect + if (bind_or_connect(sock, *rp)) { + freeaddrinfo(result); + return sock; + } - close_socket(sock); - } + close_socket(sock); + } - freeaddrinfo(result); - return INVALID_SOCKET; -} + freeaddrinfo(result); + return INVALID_SOCKET; + } -inline void set_nonblocking(socket_t sock, bool nonblocking) { + inline void set_nonblocking(socket_t sock, bool nonblocking) + { #ifdef _WIN32 - auto flags = nonblocking ? 1UL : 0UL; - ioctlsocket(sock, FIONBIO, &flags); + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); #else - auto flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, - nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, + nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); #endif -} + } -inline bool is_connection_error() { + inline bool is_connection_error() + { #ifdef _WIN32 - return WSAGetLastError() != WSAEWOULDBLOCK; + return WSAGetLastError() != WSAEWOULDBLOCK; #else - return errno != EINPROGRESS; + return errno != EINPROGRESS; #endif -} + } -inline bool bind_ip_address(socket_t sock, const char *host) { - struct addrinfo hints; - struct addrinfo *result; + inline bool bind_ip_address(socket_t sock, const char* host) + { + struct addrinfo hints; + struct addrinfo* result; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; - if (getaddrinfo(host, "0", &hints, &result)) { return false; } + if (getaddrinfo(host, "0", &hints, &result)) { + return false; + } - auto ret = false; - for (auto rp = result; rp; rp = rp->ai_next) { - const auto &ai = *rp; - if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { - ret = true; - break; - } - } + auto ret = false; + for (auto rp = result; rp; rp = rp->ai_next) { + const auto& ai = *rp; + if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + ret = true; + break; + } + } - freeaddrinfo(result); - return ret; -} + freeaddrinfo(result); + return ret; + } #if !defined _WIN32 && !defined ANDROID #define USE_IF2IP #endif #ifdef USE_IF2IP -inline std::string if2ip(const std::string &ifn) { - struct ifaddrs *ifap; - getifaddrs(&ifap); - for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr && ifn == ifa->ifa_name) { - if (ifa->ifa_addr->sa_family == AF_INET) { - auto sa = reinterpret_cast(ifa->ifa_addr); - char buf[INET_ADDRSTRLEN]; - if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { - freeifaddrs(ifap); - return std::string(buf, INET_ADDRSTRLEN); - } - } - } - } - freeifaddrs(ifap); - return std::string(); -} + inline std::string if2ip(const std::string& ifn) + { + struct ifaddrs* ifap; + getifaddrs(&ifap); + for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifn == ifa->ifa_name) { + if (ifa->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(ifa->ifa_addr); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { + freeifaddrs(ifap); + return std::string(buf, INET_ADDRSTRLEN); + } + } + } + } + freeifaddrs(ifap); + return std::string(); + } #endif -inline socket_t create_client_socket(const char *host, int port, - bool tcp_nodelay, - SocketOptions socket_options, - time_t timeout_sec, time_t timeout_usec, - const std::string &intf, Error &error) { - auto sock = create_socket( - host, port, 0, tcp_nodelay, socket_options, - [&](socket_t sock, struct addrinfo &ai) -> bool { - if (!intf.empty()) { + inline socket_t create_client_socket(const char* host, int port, + bool tcp_nodelay, + SocketOptions socket_options, + time_t timeout_sec, time_t timeout_usec, + const std::string& intf, Error& error) + { + auto sock = create_socket( + host, port, 0, tcp_nodelay, socket_options, + [&](socket_t sock, struct addrinfo& ai) -> bool { + if (!intf.empty()) { #ifdef USE_IF2IP - auto ip = if2ip(intf); - if (ip.empty()) { ip = intf; } - if (!bind_ip_address(sock, ip.c_str())) { - error = Error::BindIPAddress; - return false; - } + auto ip = if2ip(intf); + if (ip.empty()) { + ip = intf; + } + if (!bind_ip_address(sock, ip.c_str())) { + error = Error::BindIPAddress; + return false; + } #endif + } + + set_nonblocking(sock, true); + + auto ret = ::connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); + + if (ret < 0) { + if (is_connection_error() || !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) { + close_socket(sock); + error = Error::Connection; + return false; + } + } + + set_nonblocking(sock, false); + error = Error::Success; + return true; + }); + + if (sock != INVALID_SOCKET) { + error = Error::Success; + } else { + if (error == Error::Success) { + error = Error::Connection; + } } - set_nonblocking(sock, true); + return sock; + } - auto ret = - ::connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); + inline void get_remote_ip_and_port(const struct sockaddr_storage& addr, + socklen_t addr_len, std::string& ip, + int& port) + { + if (addr.ss_family == AF_INET) { + port = ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + port = ntohs(reinterpret_cast(&addr)->sin6_port); + } - if (ret < 0) { - if (is_connection_error() || - !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) { - close_socket(sock); - error = Error::Connection; - return false; - } + std::array ipstr {}; + if (!getnameinfo(reinterpret_cast(&addr), addr_len, + ipstr.data(), static_cast(ipstr.size()), nullptr, + 0, NI_NUMERICHOST)) { + ip = ipstr.data(); } + } - set_nonblocking(sock, false); - error = Error::Success; - return true; - }); - - if (sock != INVALID_SOCKET) { - error = Error::Success; - } else { - if (error == Error::Success) { error = Error::Connection; } - } - - return sock; -} - -inline void get_remote_ip_and_port(const struct sockaddr_storage &addr, - socklen_t addr_len, std::string &ip, - int &port) { - if (addr.ss_family == AF_INET) { - port = ntohs(reinterpret_cast(&addr)->sin_port); - } else if (addr.ss_family == AF_INET6) { - port = - ntohs(reinterpret_cast(&addr)->sin6_port); - } - - std::array ipstr{}; - if (!getnameinfo(reinterpret_cast(&addr), addr_len, - ipstr.data(), static_cast(ipstr.size()), nullptr, - 0, NI_NUMERICHOST)) { - ip = ipstr.data(); - } -} - -inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - - if (!getpeername(sock, reinterpret_cast(&addr), - &addr_len)) { - get_remote_ip_and_port(addr, addr_len, ip, port); - } -} - -inline const char * -find_content_type(const std::string &path, - const std::map &user_data) { - auto ext = file_extension(path); - - auto it = user_data.find(ext); - if (it != user_data.end()) { return it->second.c_str(); } - - if (ext == "txt") { - return "text/plain"; - } else if (ext == "html" || ext == "htm") { - return "text/html"; - } else if (ext == "css") { - return "text/css"; - } else if (ext == "jpeg" || ext == "jpg") { - return "image/jpg"; - } else if (ext == "png") { - return "image/png"; - } else if (ext == "gif") { - return "image/gif"; - } else if (ext == "svg") { - return "image/svg+xml"; - } else if (ext == "ico") { - return "image/x-icon"; - } else if (ext == "json") { - return "application/json"; - } else if (ext == "pdf") { - return "application/pdf"; - } else if (ext == "js") { - return "application/javascript"; - } else if (ext == "wasm") { - return "application/wasm"; - } else if (ext == "xml") { - return "application/xml"; - } else if (ext == "xhtml") { - return "application/xhtml+xml"; - } - return nullptr; -} - -inline const char *status_message(int status) { - switch (status) { - case 100: return "Continue"; - case 101: return "Switching Protocol"; - case 102: return "Processing"; - case 103: return "Early Hints"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 208: return "Already Reported"; - case 226: return "IM Used"; - case 300: return "Multiple Choice"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 306: return "unused"; - case 307: return "Temporary Redirect"; - case 308: return "Permanent Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Payload Too Large"; - case 414: return "URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 418: return "I'm a teapot"; - case 421: return "Misdirected Request"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 425: return "Too Early"; - case 426: return "Upgrade Required"; - case 428: return "Precondition Required"; - case 429: return "Too Many Requests"; - case 431: return "Request Header Fields Too Large"; - case 451: return "Unavailable For Legal Reasons"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - case 506: return "Variant Also Negotiates"; - case 507: return "Insufficient Storage"; - case 508: return "Loop Detected"; - case 510: return "Not Extended"; - case 511: return "Network Authentication Required"; - - default: - case 500: return "Internal Server Error"; - } -} - -inline bool can_compress_content_type(const std::string &content_type) { - return (!content_type.find("text/") && content_type != "text/event-stream") || - content_type == "image/svg+xml" || - content_type == "application/javascript" || - content_type == "application/json" || - content_type == "application/xml" || - content_type == "application/xhtml+xml"; -} - -enum class EncodingType { None = 0, Gzip, Brotli }; - -inline EncodingType encoding_type(const Request &req, const Response &res) { - auto ret = - detail::can_compress_content_type(res.get_header_value("Content-Type")); - if (!ret) { return EncodingType::None; } - - const auto &s = req.get_header_value("Accept-Encoding"); - (void)(s); + inline void get_remote_ip_and_port(socket_t sock, std::string& ip, int& port) + { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (!getpeername(sock, reinterpret_cast(&addr), + &addr_len)) { + get_remote_ip_and_port(addr, addr_len, ip, port); + } + } + + inline const char* + find_content_type(const std::string& path, + const std::map& user_data) + { + auto ext = file_extension(path); + + auto it = user_data.find(ext); + if (it != user_data.end()) { + return it->second.c_str(); + } + + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html" || ext == "htm") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "wasm") { + return "application/wasm"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; + } + return nullptr; + } + + inline const char* status_message(int status) + { + switch (status) { + case 100: + return "Continue"; + case 101: + return "Switching Protocol"; + case 102: + return "Processing"; + case 103: + return "Early Hints"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + case 207: + return "Multi-Status"; + case 208: + return "Already Reported"; + case 226: + return "IM Used"; + case 300: + return "Multiple Choice"; + case 301: + return "Moved Permanently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 306: + return "unused"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 402: + return "Payment Required"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Payload Too Large"; + case 414: + return "URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Range Not Satisfiable"; + case 417: + return "Expectation Failed"; + case 418: + return "I'm a teapot"; + case 421: + return "Misdirected Request"; + case 422: + return "Unprocessable Entity"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 425: + return "Too Early"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + case 451: + return "Unavailable For Legal Reasons"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; + + default: + case 500: + return "Internal Server Error"; + } + } + + inline bool can_compress_content_type(const std::string& content_type) + { + return (!content_type.find("text/") && content_type != "text/event-stream") || content_type == "image/svg+xml" || content_type == "application/javascript" || content_type == "application/json" || content_type == "application/xml" || content_type == "application/xhtml+xml"; + } + + enum class EncodingType { None = 0, + Gzip, + Brotli }; + + inline EncodingType encoding_type(const Request& req, const Response& res) + { + auto ret = detail::can_compress_content_type(res.get_header_value("Content-Type")); + if (!ret) { + return EncodingType::None; + } + + const auto& s = req.get_header_value("Accept-Encoding"); + (void)(s); #ifdef CPPHTTPLIB_BROTLI_SUPPORT - // TODO: 'Accept-Encoding' has br, not br;q=0 - ret = s.find("br") != std::string::npos; - if (ret) { return EncodingType::Brotli; } + // TODO: 'Accept-Encoding' has br, not br;q=0 + ret = s.find("br") != std::string::npos; + if (ret) { + return EncodingType::Brotli; + } #endif #ifdef CPPHTTPLIB_ZLIB_SUPPORT - // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 - ret = s.find("gzip") != std::string::npos; - if (ret) { return EncodingType::Gzip; } + // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 + ret = s.find("gzip") != std::string::npos; + if (ret) { + return EncodingType::Gzip; + } #endif - return EncodingType::None; -} + return EncodingType::None; + } -class compressor { -public: - virtual ~compressor(){}; + class compressor { + public: + virtual ~compressor() {}; - typedef std::function Callback; - virtual bool compress(const char *data, size_t data_length, bool last, - Callback callback) = 0; -}; + typedef std::function Callback; + virtual bool compress(const char* data, size_t data_length, bool last, + Callback callback) + = 0; + }; -class decompressor { -public: - virtual ~decompressor() {} + class decompressor { + public: + virtual ~decompressor() { } - virtual bool is_valid() const = 0; + virtual bool is_valid() const = 0; - typedef std::function Callback; - virtual bool decompress(const char *data, size_t data_length, - Callback callback) = 0; -}; + typedef std::function Callback; + virtual bool decompress(const char* data, size_t data_length, + Callback callback) + = 0; + }; -class nocompressor : public compressor { -public: - ~nocompressor(){}; + class nocompressor : public compressor { + public: + ~nocompressor() {}; - bool compress(const char *data, size_t data_length, bool /*last*/, - Callback callback) override { - if (!data_length) { return true; } - return callback(data, data_length); - } -}; + bool compress(const char* data, size_t data_length, bool /*last*/, + Callback callback) override + { + if (!data_length) { + return true; + } + return callback(data, data_length); + } + }; #ifdef CPPHTTPLIB_ZLIB_SUPPORT -class gzip_compressor : public compressor { -public: - gzip_compressor() { - std::memset(&strm_, 0, sizeof(strm_)); - strm_.zalloc = Z_NULL; - strm_.zfree = Z_NULL; - strm_.opaque = Z_NULL; + class gzip_compressor : public compressor { + public: + gzip_compressor() + { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, + Z_DEFAULT_STRATEGY) + == Z_OK; + } - is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, - Z_DEFAULT_STRATEGY) == Z_OK; - } + ~gzip_compressor() { deflateEnd(&strm_); } - ~gzip_compressor() { deflateEnd(&strm_); } + bool compress(const char* data, size_t data_length, bool last, + Callback callback) override + { + assert(is_valid_); - bool compress(const char *data, size_t data_length, bool last, - Callback callback) override { - assert(is_valid_); + auto flush = last ? Z_FINISH : Z_NO_FLUSH; - auto flush = last ? Z_FINISH : Z_NO_FLUSH; + strm_.avail_in = static_cast(data_length); + strm_.next_in = const_cast(reinterpret_cast(data)); - strm_.avail_in = static_cast(data_length); - strm_.next_in = const_cast(reinterpret_cast(data)); + int ret = Z_OK; - int ret = Z_OK; + std::array buff {}; + do { + strm_.avail_out = buff.size(); + strm_.next_out = reinterpret_cast(buff.data()); - std::array buff{}; - do { - strm_.avail_out = buff.size(); - strm_.next_out = reinterpret_cast(buff.data()); + ret = deflate(&strm_, flush); + assert(ret != Z_STREAM_ERROR); - ret = deflate(&strm_, flush); - assert(ret != Z_STREAM_ERROR); + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } while (strm_.avail_out == 0); - if (!callback(buff.data(), buff.size() - strm_.avail_out)) { - return false; - } - } while (strm_.avail_out == 0); + assert((last && ret == Z_STREAM_END) || (!last && ret == Z_OK)); + assert(strm_.avail_in == 0); + return true; + } - assert((last && ret == Z_STREAM_END) || (!last && ret == Z_OK)); - assert(strm_.avail_in == 0); - return true; - } + private: + bool is_valid_ = false; + z_stream strm_; + }; -private: - bool is_valid_ = false; - z_stream strm_; -}; + class gzip_decompressor : public decompressor { + public: + gzip_decompressor() + { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 32 specifies + // that the stream type should be automatically detected either gzip or + // deflate. + is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK; + } -class gzip_decompressor : public decompressor { -public: - gzip_decompressor() { - std::memset(&strm_, 0, sizeof(strm_)); - strm_.zalloc = Z_NULL; - strm_.zfree = Z_NULL; - strm_.opaque = Z_NULL; - - // 15 is the value of wbits, which should be at the maximum possible value - // to ensure that any gzip stream can be decoded. The offset of 32 specifies - // that the stream type should be automatically detected either gzip or - // deflate. - is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK; - } - - ~gzip_decompressor() { inflateEnd(&strm_); } - - bool is_valid() const override { return is_valid_; } - - bool decompress(const char *data, size_t data_length, - Callback callback) override { - assert(is_valid_); - - int ret = Z_OK; - - strm_.avail_in = static_cast(data_length); - strm_.next_in = const_cast(reinterpret_cast(data)); - - std::array buff{}; - while (strm_.avail_in > 0) { - strm_.avail_out = buff.size(); - strm_.next_out = reinterpret_cast(buff.data()); - - ret = inflate(&strm_, Z_NO_FLUSH); - assert(ret != Z_STREAM_ERROR); - switch (ret) { - case Z_NEED_DICT: - case Z_DATA_ERROR: - case Z_MEM_ERROR: inflateEnd(&strm_); return false; - } - - if (!callback(buff.data(), buff.size() - strm_.avail_out)) { - return false; - } - } + ~gzip_decompressor() { inflateEnd(&strm_); } - return ret == Z_OK || ret == Z_STREAM_END; - } + bool is_valid() const override { return is_valid_; } -private: - bool is_valid_ = false; - z_stream strm_; -}; + bool decompress(const char* data, size_t data_length, + Callback callback) override + { + assert(is_valid_); + + int ret = Z_OK; + + strm_.avail_in = static_cast(data_length); + strm_.next_in = const_cast(reinterpret_cast(data)); + + std::array buff {}; + while (strm_.avail_in > 0) { + strm_.avail_out = buff.size(); + strm_.next_out = reinterpret_cast(buff.data()); + + ret = inflate(&strm_, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&strm_); + return false; + } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } + + return ret == Z_OK || ret == Z_STREAM_END; + } + + private: + bool is_valid_ = false; + z_stream strm_; + }; #endif #ifdef CPPHTTPLIB_BROTLI_SUPPORT -class brotli_compressor : public compressor { -public: - brotli_compressor() { - state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); - } - - ~brotli_compressor() { BrotliEncoderDestroyInstance(state_); } + class brotli_compressor : public compressor { + public: + brotli_compressor() + { + state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); + } - bool compress(const char *data, size_t data_length, bool last, - Callback callback) override { - std::array buff{}; + ~brotli_compressor() { BrotliEncoderDestroyInstance(state_); } - auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS; - auto available_in = data_length; - auto next_in = reinterpret_cast(data); + bool compress(const char* data, size_t data_length, bool last, + Callback callback) override + { + std::array buff {}; + + auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS; + auto available_in = data_length; + auto next_in = reinterpret_cast(data); + + for (;;) { + if (last) { + if (BrotliEncoderIsFinished(state_)) { + break; + } + } else { + if (!available_in) { + break; + } + } + + auto available_out = buff.size(); + auto next_out = buff.data(); + + if (!BrotliEncoderCompressStream(state_, operation, &available_in, + &next_in, &available_out, &next_out, + nullptr)) { + return false; + } + + auto output_bytes = buff.size() - available_out; + if (output_bytes) { + callback(reinterpret_cast(buff.data()), output_bytes); + } + } - for (;;) { - if (last) { - if (BrotliEncoderIsFinished(state_)) { break; } - } else { - if (!available_in) { break; } - } + return true; + } - auto available_out = buff.size(); - auto next_out = buff.data(); + private: + BrotliEncoderState* state_ = nullptr; + }; - if (!BrotliEncoderCompressStream(state_, operation, &available_in, - &next_in, &available_out, &next_out, - nullptr)) { - return false; - } + class brotli_decompressor : public decompressor { + public: + brotli_decompressor() + { + decoder_s = BrotliDecoderCreateInstance(0, 0, 0); + decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT + : BROTLI_DECODER_RESULT_ERROR; + } - auto output_bytes = buff.size() - available_out; - if (output_bytes) { - callback(reinterpret_cast(buff.data()), output_bytes); - } - } + ~brotli_decompressor() + { + if (decoder_s) { + BrotliDecoderDestroyInstance(decoder_s); + } + } - return true; - } + bool is_valid() const override { return decoder_s; } -private: - BrotliEncoderState *state_ = nullptr; -}; + bool decompress(const char* data, size_t data_length, + Callback callback) override + { + if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_ERROR) { + return 0; + } -class brotli_decompressor : public decompressor { -public: - brotli_decompressor() { - decoder_s = BrotliDecoderCreateInstance(0, 0, 0); - decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT - : BROTLI_DECODER_RESULT_ERROR; - } + const uint8_t* next_in = (const uint8_t*)data; + size_t avail_in = data_length; + size_t total_out; - ~brotli_decompressor() { - if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); } - } + decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; - bool is_valid() const override { return decoder_s; } + std::array buff {}; + while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { + char* next_out = buff.data(); + size_t avail_out = buff.size(); - bool decompress(const char *data, size_t data_length, - Callback callback) override { - if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || - decoder_r == BROTLI_DECODER_RESULT_ERROR) { - return 0; - } + decoder_r = BrotliDecoderDecompressStream( + decoder_s, &avail_in, &next_in, &avail_out, + reinterpret_cast(&next_out), &total_out); - const uint8_t *next_in = (const uint8_t *)data; - size_t avail_in = data_length; - size_t total_out; + if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { + return false; + } - decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + if (!callback(buff.data(), buff.size() - avail_out)) { + return false; + } + } - std::array buff{}; - while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { - char *next_out = buff.data(); - size_t avail_out = buff.size(); + return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; + } - decoder_r = BrotliDecoderDecompressStream( - decoder_s, &avail_in, &next_in, &avail_out, - reinterpret_cast(&next_out), &total_out); + private: + BrotliDecoderResult decoder_r; + BrotliDecoderState* decoder_s = nullptr; + }; +#endif - if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; } + inline bool has_header(const Headers& headers, const char* key) + { + return headers.find(key) != headers.end(); + } - if (!callback(buff.data(), buff.size() - avail_out)) { return false; } + inline const char* get_header_value(const Headers& headers, const char* key, + size_t id = 0, const char* def = nullptr) + { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return it->second.c_str(); + } + return def; } - return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || - decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; - } + template + inline T get_header_value(const Headers& /*headers*/, const char* /*key*/, + size_t /*id*/ = 0, uint64_t /*def*/ = 0) { } -private: - BrotliDecoderResult decoder_r; - BrotliDecoderState *decoder_s = nullptr; -}; -#endif + template <> + inline uint64_t get_header_value(const Headers& headers, + const char* key, size_t id, + uint64_t def) + { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; + } -inline bool has_header(const Headers &headers, const char *key) { - return headers.find(key) != headers.end(); -} + template + inline bool parse_header(const char* beg, const char* end, T fn) + { + // Skip trailing spaces and tabs. + while (beg < end && is_space_or_tab(end[-1])) { + end--; + } -inline const char *get_header_value(const Headers &headers, const char *key, - size_t id = 0, const char *def = nullptr) { - auto rng = headers.equal_range(key); - auto it = rng.first; - std::advance(it, static_cast(id)); - if (it != rng.second) { return it->second.c_str(); } - return def; -} + auto p = beg; + while (p < end && *p != ':') { + p++; + } -template -inline T get_header_value(const Headers & /*headers*/, const char * /*key*/, - size_t /*id*/ = 0, uint64_t /*def*/ = 0) {} - -template <> -inline uint64_t get_header_value(const Headers &headers, - const char *key, size_t id, - uint64_t def) { - auto rng = headers.equal_range(key); - auto it = rng.first; - std::advance(it, static_cast(id)); - if (it != rng.second) { - return std::strtoull(it->second.data(), nullptr, 10); - } - return def; -} + if (p == end) { + return false; + } -template -inline bool parse_header(const char *beg, const char *end, T fn) { - // Skip trailing spaces and tabs. - while (beg < end && is_space_or_tab(end[-1])) { - end--; - } + auto key_end = p; - auto p = beg; - while (p < end && *p != ':') { - p++; - } + if (*p++ != ':') { + return false; + } - if (p == end) { return false; } + while (p < end && is_space_or_tab(*p)) { + p++; + } - auto key_end = p; + if (p < end) { + fn(std::string(beg, key_end), decode_url(std::string(p, end), false)); + return true; + } - if (*p++ != ':') { return false; } + return false; + } - while (p < end && is_space_or_tab(*p)) { - p++; - } + inline bool read_headers(Stream& strm, Headers& headers) + { + const auto bufsiz = 2048; + char buf[bufsiz]; + stream_line_reader line_reader(strm, buf, bufsiz); - if (p < end) { - fn(std::string(beg, key_end), decode_url(std::string(p, end), false)); - return true; - } + for (;;) { + if (!line_reader.getline()) { + return false; + } - return false; -} + // Check if the line ends with CRLF. + if (line_reader.end_with_crlf()) { + // Blank line indicates end of headers. + if (line_reader.size() == 2) { + break; + } + } else { + continue; // Skip invalid line. + } -inline bool read_headers(Stream &strm, Headers &headers) { - const auto bufsiz = 2048; - char buf[bufsiz]; - stream_line_reader line_reader(strm, buf, bufsiz); + // Exclude CRLF + auto end = line_reader.ptr() + line_reader.size() - 2; - for (;;) { - if (!line_reader.getline()) { return false; } + parse_header(line_reader.ptr(), end, + [&](std::string&& key, std::string&& val) { + headers.emplace(std::move(key), std::move(val)); + }); + } - // Check if the line ends with CRLF. - if (line_reader.end_with_crlf()) { - // Blank line indicates end of headers. - if (line_reader.size() == 2) { break; } - } else { - continue; // Skip invalid line. + return true; } - // Exclude CRLF - auto end = line_reader.ptr() + line_reader.size() - 2; - - parse_header(line_reader.ptr(), end, - [&](std::string &&key, std::string &&val) { - headers.emplace(std::move(key), std::move(val)); - }); - } - - return true; -} - -inline bool read_content_with_length(Stream &strm, uint64_t len, - Progress progress, ContentReceiver out) { - char buf[CPPHTTPLIB_RECV_BUFSIZ]; + inline bool read_content_with_length(Stream& strm, uint64_t len, + Progress progress, ContentReceiver out) + { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { + return false; + } - uint64_t r = 0; - while (r < len) { - auto read_len = static_cast(len - r); - auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); - if (n <= 0) { return false; } + if (!out(buf, static_cast(n))) { + return false; + } - if (!out(buf, static_cast(n))) { return false; } + r += static_cast(n); - r += static_cast(n); + if (progress) { + if (!progress(r, len)) { + return false; + } + } + } - if (progress) { - if (!progress(r, len)) { return false; } + return true; } - } - return true; -} + inline void skip_content_with_length(Stream& strm, uint64_t len) + { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { + return; + } + r += static_cast(n); + } + } -inline void skip_content_with_length(Stream &strm, uint64_t len) { - char buf[CPPHTTPLIB_RECV_BUFSIZ]; - uint64_t r = 0; - while (r < len) { - auto read_len = static_cast(len - r); - auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); - if (n <= 0) { return; } - r += static_cast(n); - } -} + inline bool read_content_without_length(Stream& strm, ContentReceiver out) + { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + if (!out(buf, static_cast(n))) { + return false; + } + } -inline bool read_content_without_length(Stream &strm, ContentReceiver out) { - char buf[CPPHTTPLIB_RECV_BUFSIZ]; - for (;;) { - auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); - if (n < 0) { - return false; - } else if (n == 0) { - return true; + return true; } - if (!out(buf, static_cast(n))) { return false; } - } - - return true; -} -inline bool read_content_chunked(Stream &strm, ContentReceiver out) { - const auto bufsiz = 16; - char buf[bufsiz]; + inline bool read_content_chunked(Stream& strm, ContentReceiver out) + { + const auto bufsiz = 16; + char buf[bufsiz]; - stream_line_reader line_reader(strm, buf, bufsiz); + stream_line_reader line_reader(strm, buf, bufsiz); - if (!line_reader.getline()) { return false; } + if (!line_reader.getline()) { + return false; + } - unsigned long chunk_len; - while (true) { - char *end_ptr; + unsigned long chunk_len; + while (true) { + char* end_ptr; - chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); + chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); - if (end_ptr == line_reader.ptr()) { return false; } - if (chunk_len == ULONG_MAX) { return false; } + if (end_ptr == line_reader.ptr()) { + return false; + } + if (chunk_len == ULONG_MAX) { + return false; + } - if (chunk_len == 0) { break; } + if (chunk_len == 0) { + break; + } - if (!read_content_with_length(strm, chunk_len, nullptr, out)) { - return false; - } + if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + return false; + } - if (!line_reader.getline()) { return false; } + if (!line_reader.getline()) { + return false; + } - if (strcmp(line_reader.ptr(), "\r\n")) { break; } + if (strcmp(line_reader.ptr(), "\r\n")) { + break; + } - if (!line_reader.getline()) { return false; } - } + if (!line_reader.getline()) { + return false; + } + } - if (chunk_len == 0) { - // Reader terminator after chunks - if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) - return false; - } + if (chunk_len == 0) { + // Reader terminator after chunks + if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) + return false; + } - return true; -} + return true; + } -inline bool is_chunked_transfer_encoding(const Headers &headers) { - return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), - "chunked"); -} + inline bool is_chunked_transfer_encoding(const Headers& headers) + { + return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), + "chunked"); + } -template -bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver, - bool decompress, U callback) { - if (decompress) { - std::string encoding = x.get_header_value("Content-Encoding"); - std::shared_ptr decompressor; + template + bool prepare_content_receiver(T& x, int& status, ContentReceiver receiver, + bool decompress, U callback) + { + if (decompress) { + std::string encoding = x.get_header_value("Content-Encoding"); + std::shared_ptr decompressor; - if (encoding.find("gzip") != std::string::npos || - encoding.find("deflate") != std::string::npos) { + if (encoding.find("gzip") != std::string::npos || encoding.find("deflate") != std::string::npos) { #ifdef CPPHTTPLIB_ZLIB_SUPPORT - decompressor = std::make_shared(); + decompressor = std::make_shared(); #else - status = 415; - return false; + status = 415; + return false; #endif - } else if (encoding.find("br") != std::string::npos) { + } else if (encoding.find("br") != std::string::npos) { #ifdef CPPHTTPLIB_BROTLI_SUPPORT - decompressor = std::make_shared(); + decompressor = std::make_shared(); #else - status = 415; - return false; + status = 415; + return false; #endif - } + } + + if (decompressor) { + if (decompressor->is_valid()) { + ContentReceiver out = [&](const char* buf, size_t n) { + return decompressor->decompress( + buf, n, + [&](const char* buf, size_t n) { return receiver(buf, n); }); + }; + return callback(out); + } else { + status = 500; + return false; + } + } + } - if (decompressor) { - if (decompressor->is_valid()) { - ContentReceiver out = [&](const char *buf, size_t n) { - return decompressor->decompress( - buf, n, - [&](const char *buf, size_t n) { return receiver(buf, n); }); + ContentReceiver out = [&](const char* buf, size_t n) { + return receiver(buf, n); }; return callback(out); - } else { - status = 500; - return false; - } } - } - ContentReceiver out = [&](const char *buf, size_t n) { - return receiver(buf, n); - }; - return callback(out); -} + template + bool read_content(Stream& strm, T& x, size_t payload_max_length, int& status, + Progress progress, ContentReceiver receiver, + bool decompress) + { + return prepare_content_receiver( + x, status, receiver, decompress, [&](const ContentReceiver& out) { + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value(x.headers, "Content-Length"); + if (len > payload_max_length) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; + } else if (len > 0) { + ret = read_content_with_length(strm, len, progress, out); + } + } + + if (!ret) { + status = exceed_payload_max_length ? 413 : 400; + } + return ret; + }); + } -template -bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, - Progress progress, ContentReceiver receiver, - bool decompress) { - return prepare_content_receiver( - x, status, receiver, decompress, [&](const ContentReceiver &out) { - auto ret = true; - auto exceed_payload_max_length = false; - - if (is_chunked_transfer_encoding(x.headers)) { - ret = read_content_chunked(strm, out); - } else if (!has_header(x.headers, "Content-Length")) { - ret = read_content_without_length(strm, out); - } else { - auto len = get_header_value(x.headers, "Content-Length"); - if (len > payload_max_length) { - exceed_payload_max_length = true; - skip_content_with_length(strm, len); - ret = false; - } else if (len > 0) { - ret = read_content_with_length(strm, len, progress, out); - } + template + inline ssize_t write_headers(Stream& strm, const T& info, + const Headers& headers) + { + ssize_t write_len = 0; + for (const auto& x : info.headers) { + if (x.first == "EXCEPTION_WHAT") { + continue; + } + auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { + return len; + } + write_len += len; + } + for (const auto& x : headers) { + auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { + return len; + } + write_len += len; } + auto len = strm.write("\r\n"); + if (len < 0) { + return len; + } + write_len += len; + return write_len; + } - if (!ret) { status = exceed_payload_max_length ? 413 : 400; } - return ret; - }); -} + inline bool write_data(Stream& strm, const char* d, size_t l) + { + size_t offset = 0; + while (offset < l) { + auto length = strm.write(d + offset, l - offset); + if (length < 0) { + return false; + } + offset += static_cast(length); + } + return true; + } -template -inline ssize_t write_headers(Stream &strm, const T &info, - const Headers &headers) { - ssize_t write_len = 0; - for (const auto &x : info.headers) { - if (x.first == "EXCEPTION_WHAT") { continue; } - auto len = - strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); - if (len < 0) { return len; } - write_len += len; - } - for (const auto &x : headers) { - auto len = - strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); - if (len < 0) { return len; } - write_len += len; - } - auto len = strm.write("\r\n"); - if (len < 0) { return len; } - write_len += len; - return write_len; -} - -inline bool write_data(Stream &strm, const char *d, size_t l) { - size_t offset = 0; - while (offset < l) { - auto length = strm.write(d + offset, l - offset); - if (length < 0) { return false; } - offset += static_cast(length); - } - return true; -} + template + inline ssize_t write_content(Stream& strm, ContentProvider content_provider, + size_t offset, size_t length, T is_shutting_down) + { + size_t begin_offset = offset; + size_t end_offset = offset + length; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char* d, size_t l) { + if (ok) { + offset += l; + if (!write_data(strm, d, l)) { + ok = false; + } + } + }; -template -inline ssize_t write_content(Stream &strm, ContentProvider content_provider, - size_t offset, size_t length, T is_shutting_down) { - size_t begin_offset = offset; - size_t end_offset = offset + length; - auto ok = true; - DataSink data_sink; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (offset < end_offset && !is_shutting_down()) { + if (!content_provider(offset, end_offset - offset, data_sink)) { + return -1; + } + if (!ok) { + return -1; + } + } - data_sink.write = [&](const char *d, size_t l) { - if (ok) { - offset += l; - if (!write_data(strm, d, l)) { ok = false; } + return static_cast(offset - begin_offset); } - }; - data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + template + inline ssize_t write_content_without_length(Stream& strm, + ContentProvider content_provider, + T is_shutting_down) + { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char* d, size_t l) { + if (ok) { + offset += l; + if (!write_data(strm, d, l)) { + ok = false; + } + } + }; - while (offset < end_offset && !is_shutting_down()) { - if (!content_provider(offset, end_offset - offset, data_sink)) { - return -1; - } - if (!ok) { return -1; } - } + data_sink.done = [&](void) { data_available = false; }; - return static_cast(offset - begin_offset); -} + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; -template -inline ssize_t write_content_without_length(Stream &strm, - ContentProvider content_provider, - T is_shutting_down) { - size_t offset = 0; - auto data_available = true; - auto ok = true; - DataSink data_sink; - - data_sink.write = [&](const char *d, size_t l) { - if (ok) { - offset += l; - if (!write_data(strm, d, l)) { ok = false; } - } - }; - - data_sink.done = [&](void) { data_available = false; }; - - data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; - - while (data_available && !is_shutting_down()) { - if (!content_provider(offset, 0, data_sink)) { return -1; } - if (!ok) { return -1; } - } - - return static_cast(offset); -} - -template -inline ssize_t write_content_chunked(Stream &strm, - ContentProvider content_provider, - T is_shutting_down, U &compressor) { - size_t offset = 0; - auto data_available = true; - ssize_t total_written_length = 0; - auto ok = true; - DataSink data_sink; - - data_sink.write = [&](const char *d, size_t l) { - if (!ok) { return; } - - data_available = l > 0; - offset += l; - - std::string payload; - if (!compressor.compress(d, l, false, - [&](const char *data, size_t data_len) { - payload.append(data, data_len); - return true; - })) { - ok = false; - return; - } - - if (!payload.empty()) { - // Emit chunked response header and footer for each chunk - auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; - if (write_data(strm, chunk.data(), chunk.size())) { - total_written_length += chunk.size(); - } else { - ok = false; - return; - } - } - }; - - data_sink.done = [&](void) { - if (!ok) { return; } - - data_available = false; - - std::string payload; - if (!compressor.compress(nullptr, 0, true, - [&](const char *data, size_t data_len) { - payload.append(data, data_len); - return true; - })) { - ok = false; - return; - } - - if (!payload.empty()) { - // Emit chunked response header and footer for each chunk - auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; - if (write_data(strm, chunk.data(), chunk.size())) { - total_written_length += chunk.size(); - } else { - ok = false; - return; - } - } - - static const std::string done_marker("0\r\n\r\n"); - if (write_data(strm, done_marker.data(), done_marker.size())) { - total_written_length += done_marker.size(); - } else { - ok = false; + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { + return -1; + } + if (!ok) { + return -1; + } + } + + return static_cast(offset); } - }; - data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + template + inline ssize_t write_content_chunked(Stream& strm, + ContentProvider content_provider, + T is_shutting_down, U& compressor) + { + size_t offset = 0; + auto data_available = true; + ssize_t total_written_length = 0; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char* d, size_t l) { + if (!ok) { + return; + } - while (data_available && !is_shutting_down()) { - if (!content_provider(offset, 0, data_sink)) { return -1; } - if (!ok) { return -1; } - } + data_available = l > 0; + offset += l; - return total_written_length; -} + std::string payload; + if (!compressor.compress(d, l, false, + [&](const char* data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + ok = false; + return; + } -template -inline bool redirect(T &cli, const Request &req, Response &res, - const std::string &path) { - Request new_req = req; - new_req.path = path; - new_req.redirect_count -= 1; - - if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) { - new_req.method = "GET"; - new_req.body.clear(); - new_req.headers.clear(); - } - - Response new_res; - - auto ret = cli.send(new_req, new_res); - if (ret) { res = new_res; } - return ret; -} - -inline std::string params_to_query_str(const Params ¶ms) { - std::string query; - - for (auto it = params.begin(); it != params.end(); ++it) { - if (it != params.begin()) { query += "&"; } - query += it->first; - query += "="; - query += encode_url(it->second); - } - return query; -} - -inline void parse_query_text(const std::string &s, Params ¶ms) { - split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) { - std::string key; - std::string val; - split(b, e, '=', [&](const char *b2, const char *e2) { - if (key.empty()) { - key.assign(b2, e2); - } else { - val.assign(b2, e2); - } - }); + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (write_data(strm, chunk.data(), chunk.size())) { + total_written_length += chunk.size(); + } else { + ok = false; + return; + } + } + }; + + data_sink.done = [&](void) { + if (!ok) { + return; + } + + data_available = false; + + std::string payload; + if (!compressor.compress(nullptr, 0, true, + [&](const char* data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + ok = false; + return; + } + + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (write_data(strm, chunk.data(), chunk.size())) { + total_written_length += chunk.size(); + } else { + ok = false; + return; + } + } + + static const std::string done_marker("0\r\n\r\n"); + if (write_data(strm, done_marker.data(), done_marker.size())) { + total_written_length += done_marker.size(); + } else { + ok = false; + } + }; + + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { + return -1; + } + if (!ok) { + return -1; + } + } - if (!key.empty()) { - params.emplace(decode_url(key, true), decode_url(val, true)); + return total_written_length; } - }); -} -inline bool parse_multipart_boundary(const std::string &content_type, - std::string &boundary) { - auto pos = content_type.find("boundary="); - if (pos == std::string::npos) { return false; } - boundary = content_type.substr(pos + 9); - if (boundary.length() >= 2 && boundary.front() == '"' && - boundary.back() == '"') { - boundary = boundary.substr(1, boundary.size() - 2); - } - return !boundary.empty(); -} + template + inline bool redirect(T& cli, const Request& req, Response& res, + const std::string& path) + { + Request new_req = req; + new_req.path = path; + new_req.redirect_count -= 1; -inline bool parse_range_header(const std::string &s, Ranges &ranges) { - static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); - std::smatch m; - if (std::regex_match(s, m, re_first_range)) { - auto pos = static_cast(m.position(1)); - auto len = static_cast(m.length(1)); - bool all_valid_ranges = true; - split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { - if (!all_valid_ranges) return; - static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); - std::cmatch cm; - if (std::regex_match(b, e, cm, re_another_range)) { - ssize_t first = -1; - if (!cm.str(1).empty()) { - first = static_cast(std::stoll(cm.str(1))); + if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) { + new_req.method = "GET"; + new_req.body.clear(); + new_req.headers.clear(); } - ssize_t last = -1; - if (!cm.str(2).empty()) { - last = static_cast(std::stoll(cm.str(2))); - } + Response new_res; - if (first != -1 && last != -1 && first > last) { - all_valid_ranges = false; - return; + auto ret = cli.send(new_req, new_res); + if (ret) { + res = new_res; } - ranges.emplace_back(std::make_pair(first, last)); - } - }); - return all_valid_ranges; - } - return false; -} + return ret; + } -class MultipartFormDataParser { -public: - MultipartFormDataParser() = default; - - void set_boundary(std::string &&boundary) { boundary_ = boundary; } - - bool is_valid() const { return is_valid_; } - - template - bool parse(const char *buf, size_t n, T content_callback, U header_callback) { - - static const std::regex re_content_disposition( - "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" - "\"(.*?)\")?\\s*$", - std::regex_constants::icase); - static const std::string dash_ = "--"; - static const std::string crlf_ = "\r\n"; - - buf_.append(buf, n); // TODO: performance improvement - - while (!buf_.empty()) { - switch (state_) { - case 0: { // Initial boundary - auto pattern = dash_ + boundary_ + crlf_; - if (pattern.size() > buf_.size()) { return true; } - auto pos = buf_.find(pattern); - if (pos != 0) { return false; } - buf_.erase(0, pattern.size()); - off_ += pattern.size(); - state_ = 1; - break; - } - case 1: { // New entry - clear_file_info(); - state_ = 2; - break; - } - case 2: { // Headers - auto pos = buf_.find(crlf_); - while (pos != std::string::npos) { - // Empty line - if (pos == 0) { - if (!header_callback(file_)) { - is_valid_ = false; - return false; - } - buf_.erase(0, crlf_.size()); - off_ += crlf_.size(); - state_ = 3; - break; - } - - static const std::string header_name = "content-type:"; - const auto header = buf_.substr(0, pos); - if (start_with(header, header_name)) { - file_.content_type = trim_copy(header.substr(header_name.size())); - } else { - std::smatch m; - if (std::regex_match(header, m, re_content_disposition)) { - file_.name = m[1]; - file_.filename = m[2]; - } - } + inline std::string params_to_query_str(const Params& params) + { + std::string query; - buf_.erase(0, pos + crlf_.size()); - off_ += pos + crlf_.size(); - pos = buf_.find(crlf_); + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { + query += "&"; + } + query += it->first; + query += "="; + query += encode_url(it->second); } - if (state_ != 3) { return true; } - break; - } - case 3: { // Body - { - auto pattern = crlf_ + dash_; - if (pattern.size() > buf_.size()) { return true; } - - auto pos = buf_.find(pattern); - if (pos == std::string::npos) { - pos = buf_.size(); - while (pos > 0) { - auto c = buf_[pos - 1]; - if (c != '\r' && c != '\n' && c != '-') { break; } - pos--; + return query; + } + + inline void parse_query_text(const std::string& s, Params& params) + { + split(s.data(), s.data() + s.size(), '&', [&](const char* b, const char* e) { + std::string key; + std::string val; + split(b, e, '=', [&](const char* b2, const char* e2) { + if (key.empty()) { + key.assign(b2, e2); + } else { + val.assign(b2, e2); + } + }); + + if (!key.empty()) { + params.emplace(decode_url(key, true), decode_url(val, true)); } - } + }); + } - if (!content_callback(buf_.data(), pos)) { - is_valid_ = false; + inline bool parse_multipart_boundary(const std::string& content_type, + std::string& boundary) + { + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { return false; - } + } + boundary = content_type.substr(pos + 9); + if (boundary.length() >= 2 && boundary.front() == '"' && boundary.back() == '"') { + boundary = boundary.substr(1, boundary.size() - 2); + } + return !boundary.empty(); + } - off_ += pos; - buf_.erase(0, pos); + inline bool parse_range_header(const std::string& s, Ranges& ranges) + { + static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); + std::smatch m; + if (std::regex_match(s, m, re_first_range)) { + auto pos = static_cast(m.position(1)); + auto len = static_cast(m.length(1)); + bool all_valid_ranges = true; + split(&s[pos], &s[pos + len], ',', [&](const char* b, const char* e) { + if (!all_valid_ranges) + return; + static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); + std::cmatch cm; + if (std::regex_match(b, e, cm, re_another_range)) { + ssize_t first = -1; + if (!cm.str(1).empty()) { + first = static_cast(std::stoll(cm.str(1))); + } + + ssize_t last = -1; + if (!cm.str(2).empty()) { + last = static_cast(std::stoll(cm.str(2))); + } + + if (first != -1 && last != -1 && first > last) { + all_valid_ranges = false; + return; + } + ranges.emplace_back(std::make_pair(first, last)); + } + }); + return all_valid_ranges; } + return false; + } + class MultipartFormDataParser { + public: + MultipartFormDataParser() = default; + + void set_boundary(std::string&& boundary) { boundary_ = boundary; } + + bool is_valid() const { return is_valid_; } + + template + bool parse(const char* buf, size_t n, T content_callback, U header_callback) { - auto pattern = crlf_ + dash_ + boundary_; - if (pattern.size() > buf_.size()) { return true; } - - auto pos = buf_.find(pattern); - if (pos != std::string::npos) { - if (!content_callback(buf_.data(), pos)) { - is_valid_ = false; - return false; - } - off_ += pos + pattern.size(); - buf_.erase(0, pos + pattern.size()); - state_ = 4; - } else { - if (!content_callback(buf_.data(), pattern.size())) { - is_valid_ = false; - return false; + static const std::regex re_content_disposition( + "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" + "\"(.*?)\")?\\s*$", + std::regex_constants::icase); + static const std::string dash_ = "--"; + static const std::string crlf_ = "\r\n"; + + buf_.append(buf, n); // TODO: performance improvement + + while (!buf_.empty()) { + switch (state_) { + case 0: { // Initial boundary + auto pattern = dash_ + boundary_ + crlf_; + if (pattern.size() > buf_.size()) { + return true; + } + auto pos = buf_.find(pattern); + if (pos != 0) { + return false; + } + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + state_ = 1; + break; + } + case 1: { // New entry + clear_file_info(); + state_ = 2; + break; + } + case 2: { // Headers + auto pos = buf_.find(crlf_); + while (pos != std::string::npos) { + // Empty line + if (pos == 0) { + if (!header_callback(file_)) { + is_valid_ = false; + return false; + } + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 3; + break; + } + + static const std::string header_name = "content-type:"; + const auto header = buf_.substr(0, pos); + if (start_with(header, header_name)) { + file_.content_type = trim_copy(header.substr(header_name.size())); + } else { + std::smatch m; + if (std::regex_match(header, m, re_content_disposition)) { + file_.name = m[1]; + file_.filename = m[2]; + } + } + + buf_.erase(0, pos + crlf_.size()); + off_ += pos + crlf_.size(); + pos = buf_.find(crlf_); + } + if (state_ != 3) { + return true; + } + break; + } + case 3: { // Body + { + auto pattern = crlf_ + dash_; + if (pattern.size() > buf_.size()) { + return true; + } + + auto pos = buf_.find(pattern); + if (pos == std::string::npos) { + pos = buf_.size(); + while (pos > 0) { + auto c = buf_[pos - 1]; + if (c != '\r' && c != '\n' && c != '-') { + break; + } + pos--; + } + } + + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos; + buf_.erase(0, pos); + } + + { + auto pattern = crlf_ + dash_ + boundary_; + if (pattern.size() > buf_.size()) { + return true; + } + + auto pos = buf_.find(pattern); + if (pos != std::string::npos) { + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos + pattern.size(); + buf_.erase(0, pos + pattern.size()); + state_ = 4; + } else { + if (!content_callback(buf_.data(), pattern.size())) { + is_valid_ = false; + return false; + } + + off_ += pattern.size(); + buf_.erase(0, pattern.size()); + } + } + break; + } + case 4: { // Boundary + if (crlf_.size() > buf_.size()) { + return true; + } + if (buf_.compare(0, crlf_.size(), crlf_) == 0) { + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 1; + } else { + auto pattern = dash_ + crlf_; + if (pattern.size() > buf_.size()) { + return true; + } + if (buf_.compare(0, pattern.size(), pattern) == 0) { + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + is_valid_ = true; + state_ = 5; + } else { + return true; + } + } + break; + } + case 5: { // Done + is_valid_ = false; + return false; + } + } } - off_ += pattern.size(); - buf_.erase(0, pattern.size()); - } - } - break; - } - case 4: { // Boundary - if (crlf_.size() > buf_.size()) { return true; } - if (buf_.compare(0, crlf_.size(), crlf_) == 0) { - buf_.erase(0, crlf_.size()); - off_ += crlf_.size(); - state_ = 1; - } else { - auto pattern = dash_ + crlf_; - if (pattern.size() > buf_.size()) { return true; } - if (buf_.compare(0, pattern.size(), pattern) == 0) { - buf_.erase(0, pattern.size()); - off_ += pattern.size(); - is_valid_ = true; - state_ = 5; - } else { return true; - } } - break; - } - case 5: { // Done - is_valid_ = false; - return false; - } - } - } - - return true; - } -private: - void clear_file_info() { - file_.name.clear(); - file_.filename.clear(); - file_.content_type.clear(); - } - - std::string boundary_; - - std::string buf_; - size_t state_ = 0; - bool is_valid_ = false; - size_t off_ = 0; - MultipartFormData file_; -}; + private: + void clear_file_info() + { + file_.name.clear(); + file_.filename.clear(); + file_.content_type.clear(); + } -inline std::string to_lower(const char *beg, const char *end) { - std::string out; - auto it = beg; - while (it != end) { - out += static_cast(::tolower(*it)); - it++; - } - return out; -} + std::string boundary_; -inline std::string make_multipart_data_boundary() { - static const char data[] = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + std::string buf_; + size_t state_ = 0; + bool is_valid_ = false; + size_t off_ = 0; + MultipartFormData file_; + }; - std::random_device seed_gen; - std::mt19937 engine(seed_gen()); + inline std::string to_lower(const char* beg, const char* end) + { + std::string out; + auto it = beg; + while (it != end) { + out += static_cast(::tolower(*it)); + it++; + } + return out; + } - std::string result = "--cpp-httplib-multipart-data-"; + inline std::string make_multipart_data_boundary() + { + static const char data[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - for (auto i = 0; i < 16; i++) { - result += data[engine() % (sizeof(data) - 1)]; - } + std::random_device seed_gen; + std::mt19937 engine(seed_gen()); - return result; -} + std::string result = "--cpp-httplib-multipart-data-"; -inline std::pair -get_range_offset_and_length(const Request &req, size_t content_length, - size_t index) { - auto r = req.ranges[index]; + for (auto i = 0; i < 16; i++) { + result += data[engine() % (sizeof(data) - 1)]; + } - if (r.first == -1 && r.second == -1) { - return std::make_pair(0, content_length); - } + return result; + } - auto slen = static_cast(content_length); + inline std::pair + get_range_offset_and_length(const Request& req, size_t content_length, + size_t index) + { + auto r = req.ranges[index]; - if (r.first == -1) { - r.first = slen - r.second; - r.second = slen - 1; - } + if (r.first == -1 && r.second == -1) { + return std::make_pair(0, content_length); + } - if (r.second == -1) { r.second = slen - 1; } + auto slen = static_cast(content_length); - return std::make_pair(r.first, r.second - r.first + 1); -} + if (r.first == -1) { + r.first = slen - r.second; + r.second = slen - 1; + } -inline std::string make_content_range_header_field(size_t offset, size_t length, - size_t content_length) { - std::string field = "bytes "; - field += std::to_string(offset); - field += "-"; - field += std::to_string(offset + length - 1); - field += "/"; - field += std::to_string(content_length); - return field; -} + if (r.second == -1) { + r.second = slen - 1; + } -template -bool process_multipart_ranges_data(const Request &req, Response &res, - const std::string &boundary, - const std::string &content_type, - SToken stoken, CToken ctoken, - Content content) { - for (size_t i = 0; i < req.ranges.size(); i++) { - ctoken("--"); - stoken(boundary); - ctoken("\r\n"); - if (!content_type.empty()) { - ctoken("Content-Type: "); - stoken(content_type); - ctoken("\r\n"); + return std::make_pair(r.first, r.second - r.first + 1); } - auto offsets = get_range_offset_and_length(req, res.body.size(), i); - auto offset = offsets.first; - auto length = offsets.second; + inline std::string make_content_range_header_field(size_t offset, size_t length, + size_t content_length) + { + std::string field = "bytes "; + field += std::to_string(offset); + field += "-"; + field += std::to_string(offset + length - 1); + field += "/"; + field += std::to_string(content_length); + return field; + } - ctoken("Content-Range: "); - stoken(make_content_range_header_field(offset, length, res.body.size())); - ctoken("\r\n"); - ctoken("\r\n"); - if (!content(offset, length)) { return false; } - ctoken("\r\n"); - } + template + bool process_multipart_ranges_data(const Request& req, Response& res, + const std::string& boundary, + const std::string& content_type, + SToken stoken, CToken ctoken, + Content content) + { + for (size_t i = 0; i < req.ranges.size(); i++) { + ctoken("--"); + stoken(boundary); + ctoken("\r\n"); + if (!content_type.empty()) { + ctoken("Content-Type: "); + stoken(content_type); + ctoken("\r\n"); + } - ctoken("--"); - stoken(boundary); - ctoken("--\r\n"); + auto offsets = get_range_offset_and_length(req, res.body.size(), i); + auto offset = offsets.first; + auto length = offsets.second; - return true; -} + ctoken("Content-Range: "); + stoken(make_content_range_header_field(offset, length, res.body.size())); + ctoken("\r\n"); + ctoken("\r\n"); + if (!content(offset, length)) { + return false; + } + ctoken("\r\n"); + } -inline std::string make_multipart_ranges_data(const Request &req, Response &res, - const std::string &boundary, - const std::string &content_type) { - std::string data; + ctoken("--"); + stoken(boundary); + ctoken("--\r\n"); - process_multipart_ranges_data( - req, res, boundary, content_type, - [&](const std::string &token) { data += token; }, - [&](const char *token) { data += token; }, - [&](size_t offset, size_t length) { - data += res.body.substr(offset, length); return true; - }); + } - return data; -} + inline std::string make_multipart_ranges_data(const Request& req, Response& res, + const std::string& boundary, + const std::string& content_type) + { + std::string data; + + process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string& token) { data += token; }, + [&](const char* token) { data += token; }, + [&](size_t offset, size_t length) { + data += res.body.substr(offset, length); + return true; + }); -inline size_t -get_multipart_ranges_data_length(const Request &req, Response &res, - const std::string &boundary, - const std::string &content_type) { - size_t data_length = 0; + return data; + } - process_multipart_ranges_data( - req, res, boundary, content_type, - [&](const std::string &token) { data_length += token.size(); }, - [&](const char *token) { data_length += strlen(token); }, - [&](size_t /*offset*/, size_t length) { - data_length += length; - return true; - }); + inline size_t + get_multipart_ranges_data_length(const Request& req, Response& res, + const std::string& boundary, + const std::string& content_type) + { + size_t data_length = 0; + + process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string& token) { data_length += token.size(); }, + [&](const char* token) { data_length += strlen(token); }, + [&](size_t /*offset*/, size_t length) { + data_length += length; + return true; + }); - return data_length; -} + return data_length; + } -template -inline bool write_multipart_ranges_data(Stream &strm, const Request &req, - Response &res, - const std::string &boundary, - const std::string &content_type, - T is_shutting_down) { - return process_multipart_ranges_data( - req, res, boundary, content_type, - [&](const std::string &token) { strm.write(token); }, - [&](const char *token) { strm.write(token); }, - [&](size_t offset, size_t length) { - return write_content(strm, res.content_provider_, offset, length, - is_shutting_down) >= 0; - }); -} - -inline std::pair -get_range_offset_and_length(const Request &req, const Response &res, - size_t index) { - auto r = req.ranges[index]; - - if (r.second == -1) { - r.second = static_cast(res.content_length_) - 1; - } - - return std::make_pair(r.first, r.second - r.first + 1); -} - -inline bool expect_content(const Request &req) { - if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || - req.method == "PRI" || req.method == "DELETE") { - return true; - } - // TODO: check if Content-Length is set - return false; -} + template + inline bool write_multipart_ranges_data(Stream& strm, const Request& req, + Response& res, + const std::string& boundary, + const std::string& content_type, + T is_shutting_down) + { + return process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string& token) { strm.write(token); }, + [&](const char* token) { strm.write(token); }, + [&](size_t offset, size_t length) { + return write_content(strm, res.content_provider_, offset, length, + is_shutting_down) + >= 0; + }); + } -inline bool has_crlf(const char *s) { - auto p = s; - while (*p) { - if (*p == '\r' || *p == '\n') { return true; } - p++; - } - return false; -} + inline std::pair + get_range_offset_and_length(const Request& req, const Response& res, + size_t index) + { + auto r = req.ranges[index]; + + if (r.second == -1) { + r.second = static_cast(res.content_length_) - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); + } + + inline bool expect_content(const Request& req) + { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI" || req.method == "DELETE") { + return true; + } + // TODO: check if Content-Length is set + return false; + } + + inline bool has_crlf(const char* s) + { + auto p = s; + while (*p) { + if (*p == '\r' || *p == '\n') { + return true; + } + p++; + } + return false; + } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -template -inline std::string message_digest(const std::string &s, Init init, - Update update, Final final, - size_t digest_length) { - using namespace std; + template + inline std::string message_digest(const std::string& s, Init init, + Update update, Final final, + size_t digest_length) + { + using namespace std; - std::vector md(digest_length, 0); - CTX ctx; - init(&ctx); - update(&ctx, s.data(), s.size()); - final(md.data(), &ctx); + std::vector md(digest_length, 0); + CTX ctx; + init(&ctx); + update(&ctx, s.data(), s.size()); + final(md.data(), &ctx); - stringstream ss; - for (auto c : md) { - ss << setfill('0') << setw(2) << hex << (unsigned int)c; - } - return ss.str(); -} + stringstream ss; + for (auto c : md) { + ss << setfill('0') << setw(2) << hex << (unsigned int)c; + } + return ss.str(); + } -inline std::string MD5(const std::string &s) { - return message_digest(s, MD5_Init, MD5_Update, MD5_Final, - MD5_DIGEST_LENGTH); -} + inline std::string MD5(const std::string& s) + { + return message_digest(s, MD5_Init, MD5_Update, MD5_Final, + MD5_DIGEST_LENGTH); + } -inline std::string SHA_256(const std::string &s) { - return message_digest(s, SHA256_Init, SHA256_Update, SHA256_Final, - SHA256_DIGEST_LENGTH); -} + inline std::string SHA_256(const std::string& s) + { + return message_digest(s, SHA256_Init, SHA256_Update, SHA256_Final, + SHA256_DIGEST_LENGTH); + } -inline std::string SHA_512(const std::string &s) { - return message_digest(s, SHA512_Init, SHA512_Update, SHA512_Final, - SHA512_DIGEST_LENGTH); -} + inline std::string SHA_512(const std::string& s) + { + return message_digest(s, SHA512_Init, SHA512_Update, SHA512_Final, + SHA512_DIGEST_LENGTH); + } #endif #ifdef _WIN32 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -// NOTE: This code came up with the following stackoverflow post: -// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store -inline bool load_system_certs_on_windows(X509_STORE *store) { - auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); + // NOTE: This code came up with the following stackoverflow post: + // https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store + inline bool load_system_certs_on_windows(X509_STORE* store) + { + auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); - if (!hStore) { return false; } + if (!hStore) { + return false; + } - PCCERT_CONTEXT pContext = NULL; - while (pContext = CertEnumCertificatesInStore(hStore, pContext)) { - auto encoded_cert = - static_cast(pContext->pbCertEncoded); + PCCERT_CONTEXT pContext = NULL; + while (pContext = CertEnumCertificatesInStore(hStore, pContext)) { + auto encoded_cert = static_cast(pContext->pbCertEncoded); - auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); - if (x509) { - X509_STORE_add_cert(store, x509); - X509_free(x509); - } - } + auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + } + } - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); - return true; -} + return true; + } #endif -class WSInit { -public: - WSInit() { - WSADATA wsaData; - WSAStartup(0x0002, &wsaData); - } + class WSInit { + public: + WSInit() + { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } - ~WSInit() { WSACleanup(); } -}; + ~WSInit() { WSACleanup(); } + }; -static WSInit wsinit_; + static WSInit wsinit_; #endif #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline std::pair make_digest_authentication_header( - const Request &req, const std::map &auth, - size_t cnonce_count, const std::string &cnonce, const std::string &username, - const std::string &password, bool is_proxy = false) { - using namespace std; - - string nc; - { - stringstream ss; - ss << setfill('0') << setw(8) << hex << cnonce_count; - nc = ss.str(); - } - - auto qop = auth.at("qop"); - if (qop.find("auth-int") != std::string::npos) { - qop = "auth-int"; - } else { - qop = "auth"; - } - - std::string algo = "MD5"; - if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); } - - string response; - { - auto H = algo == "SHA-256" - ? detail::SHA_256 - : algo == "SHA-512" ? detail::SHA_512 : detail::MD5; - - auto A1 = username + ":" + auth.at("realm") + ":" + password; - - auto A2 = req.method + ":" + req.path; - if (qop == "auth-int") { A2 += ":" + H(req.body); } - - response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + - ":" + qop + ":" + H(A2)); - } - - auto field = "Digest username=\"" + username + "\", realm=\"" + - auth.at("realm") + "\", nonce=\"" + auth.at("nonce") + - "\", uri=\"" + req.path + "\", algorithm=" + algo + - ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + cnonce + - "\", response=\"" + response + "\""; - - auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; - return std::make_pair(key, field); -} + inline std::pair make_digest_authentication_header( + const Request& req, const std::map& auth, + size_t cnonce_count, const std::string& cnonce, const std::string& username, + const std::string& password, bool is_proxy = false) + { + using namespace std; + + string nc; + { + stringstream ss; + ss << setfill('0') << setw(8) << hex << cnonce_count; + nc = ss.str(); + } + + auto qop = auth.at("qop"); + if (qop.find("auth-int") != std::string::npos) { + qop = "auth-int"; + } else { + qop = "auth"; + } + + std::string algo = "MD5"; + if (auth.find("algorithm") != auth.end()) { + algo = auth.at("algorithm"); + } + + string response; + { + auto H = algo == "SHA-256" + ? detail::SHA_256 + : algo == "SHA-512" ? detail::SHA_512 + : detail::MD5; + + auto A1 = username + ":" + auth.at("realm") + ":" + password; + + auto A2 = req.method + ":" + req.path; + if (qop == "auth-int") { + A2 += ":" + H(req.body); + } + + response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":" + H(A2)); + } + + auto field = "Digest username=\"" + username + "\", realm=\"" + auth.at("realm") + "\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path + "\", algorithm=" + algo + ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + cnonce + "\", response=\"" + response + "\""; + + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); + } #endif -inline bool parse_www_authenticate(const Response &res, - std::map &auth, - bool is_proxy) { - auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; - if (res.has_header(auth_key)) { - static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); - auto s = res.get_header_value(auth_key); - auto pos = s.find(' '); - if (pos != std::string::npos) { - auto type = s.substr(0, pos); - if (type == "Basic") { - return false; - } else if (type == "Digest") { - s = s.substr(pos + 1); - auto beg = std::sregex_iterator(s.begin(), s.end(), re); - for (auto i = beg; i != std::sregex_iterator(); ++i) { - auto m = *i; - auto key = s.substr(static_cast(m.position(1)), - static_cast(m.length(1))); - auto val = m.length(2) > 0 - ? s.substr(static_cast(m.position(2)), - static_cast(m.length(2))) - : s.substr(static_cast(m.position(3)), - static_cast(m.length(3))); - auth[key] = val; + inline bool parse_www_authenticate(const Response& res, + std::map& auth, + bool is_proxy) + { + auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; + if (res.has_header(auth_key)) { + static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); + auto s = res.get_header_value(auth_key); + auto pos = s.find(' '); + if (pos != std::string::npos) { + auto type = s.substr(0, pos); + if (type == "Basic") { + return false; + } else if (type == "Digest") { + s = s.substr(pos + 1); + auto beg = std::sregex_iterator(s.begin(), s.end(), re); + for (auto i = beg; i != std::sregex_iterator(); ++i) { + auto m = *i; + auto key = s.substr(static_cast(m.position(1)), + static_cast(m.length(1))); + auto val = m.length(2) > 0 + ? s.substr(static_cast(m.position(2)), + static_cast(m.length(2))) + : s.substr(static_cast(m.position(3)), + static_cast(m.length(3))); + auth[key] = val; + } + return true; + } + } } - return true; - } + return false; } - } - return false; -} -// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 -inline std::string random_string(size_t length) { - auto randchar = []() -> char { - const char charset[] = "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; - const size_t max_index = (sizeof(charset) - 1); - return charset[static_cast(rand()) % max_index]; - }; - std::string str(length, 0); - std::generate_n(str.begin(), length, randchar); - return str; -} + // https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 + inline std::string random_string(size_t length) + { + auto randchar = []() -> char { + const char charset[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + const size_t max_index = (sizeof(charset) - 1); + return charset[static_cast(rand()) % max_index]; + }; + std::string str(length, 0); + std::generate_n(str.begin(), length, randchar); + return str; + } -class ContentProviderAdapter { -public: - explicit ContentProviderAdapter( - ContentProviderWithoutLength &&content_provider) - : content_provider_(content_provider) {} + class ContentProviderAdapter { + public: + explicit ContentProviderAdapter( + ContentProviderWithoutLength&& content_provider) + : content_provider_(content_provider) + { + } - bool operator()(size_t offset, size_t, DataSink &sink) { - return content_provider_(offset, sink); - } + bool operator()(size_t offset, size_t, DataSink& sink) + { + return content_provider_(offset, sink); + } -private: - ContentProviderWithoutLength content_provider_; -}; + private: + ContentProviderWithoutLength content_provider_; + }; } // namespace detail // Header utilities -inline std::pair make_range_header(Ranges ranges) { - std::string field = "bytes="; - auto i = 0; - for (auto r : ranges) { - if (i != 0) { field += ", "; } - if (r.first != -1) { field += std::to_string(r.first); } - field += '-'; - if (r.second != -1) { field += std::to_string(r.second); } - i++; - } - return std::make_pair("Range", field); +inline std::pair make_range_header(Ranges ranges) +{ + std::string field = "bytes="; + auto i = 0; + for (auto r : ranges) { + if (i != 0) { + field += ", "; + } + if (r.first != -1) { + field += std::to_string(r.first); + } + field += '-'; + if (r.second != -1) { + field += std::to_string(r.second); + } + i++; + } + return std::make_pair("Range", field); } inline std::pair -make_basic_authentication_header(const std::string &username, - const std::string &password, - bool is_proxy = false) { - auto field = "Basic " + detail::base64_encode(username + ":" + password); - auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; - return std::make_pair(key, field); +make_basic_authentication_header(const std::string& username, + const std::string& password, + bool is_proxy = false) +{ + auto field = "Basic " + detail::base64_encode(username + ":" + password); + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); } inline std::pair -make_bearer_token_authentication_header(const std::string &token, - bool is_proxy = false) { - auto field = "Bearer " + token; - auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; - return std::make_pair(key, field); +make_bearer_token_authentication_header(const std::string& token, + bool is_proxy = false) +{ + auto field = "Bearer " + token; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); } // Request implementation -inline bool Request::has_header(const char *key) const { - return detail::has_header(headers, key); +inline bool Request::has_header(const char* key) const +{ + return detail::has_header(headers, key); } -inline std::string Request::get_header_value(const char *key, size_t id) const { - return detail::get_header_value(headers, key, id, ""); +inline std::string Request::get_header_value(const char* key, size_t id) const +{ + return detail::get_header_value(headers, key, id, ""); } template -inline T Request::get_header_value(const char *key, size_t id) const { - return detail::get_header_value(headers, key, id, 0); +inline T Request::get_header_value(const char* key, size_t id) const +{ + return detail::get_header_value(headers, key, id, 0); } -inline size_t Request::get_header_value_count(const char *key) const { - auto r = headers.equal_range(key); - return static_cast(std::distance(r.first, r.second)); +inline size_t Request::get_header_value_count(const char* key) const +{ + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); } -inline void Request::set_header(const char *key, const char *val) { - if (!detail::has_crlf(key) && !detail::has_crlf(val)) { - headers.emplace(key, val); - } +inline void Request::set_header(const char* key, const char* val) +{ + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } } -inline void Request::set_header(const char *key, const std::string &val) { - if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { - headers.emplace(key, val); - } +inline void Request::set_header(const char* key, const std::string& val) +{ + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } } -inline bool Request::has_param(const char *key) const { - return params.find(key) != params.end(); +inline bool Request::has_param(const char* key) const +{ + return params.find(key) != params.end(); } -inline std::string Request::get_param_value(const char *key, size_t id) const { - auto rng = params.equal_range(key); - auto it = rng.first; - std::advance(it, static_cast(id)); - if (it != rng.second) { return it->second; } - return std::string(); +inline std::string Request::get_param_value(const char* key, size_t id) const +{ + auto rng = params.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return it->second; + } + return std::string(); } -inline size_t Request::get_param_value_count(const char *key) const { - auto r = params.equal_range(key); - return static_cast(std::distance(r.first, r.second)); +inline size_t Request::get_param_value_count(const char* key) const +{ + auto r = params.equal_range(key); + return static_cast(std::distance(r.first, r.second)); } -inline bool Request::is_multipart_form_data() const { - const auto &content_type = get_header_value("Content-Type"); - return !content_type.find("multipart/form-data"); +inline bool Request::is_multipart_form_data() const +{ + const auto& content_type = get_header_value("Content-Type"); + return !content_type.find("multipart/form-data"); } -inline bool Request::has_file(const char *key) const { - return files.find(key) != files.end(); +inline bool Request::has_file(const char* key) const +{ + return files.find(key) != files.end(); } -inline MultipartFormData Request::get_file_value(const char *key) const { - auto it = files.find(key); - if (it != files.end()) { return it->second; } - return MultipartFormData(); +inline MultipartFormData Request::get_file_value(const char* key) const +{ + auto it = files.find(key); + if (it != files.end()) { + return it->second; + } + return MultipartFormData(); } // Response implementation -inline bool Response::has_header(const char *key) const { - return headers.find(key) != headers.end(); +inline bool Response::has_header(const char* key) const +{ + return headers.find(key) != headers.end(); } -inline std::string Response::get_header_value(const char *key, - size_t id) const { - return detail::get_header_value(headers, key, id, ""); +inline std::string Response::get_header_value(const char* key, + size_t id) const +{ + return detail::get_header_value(headers, key, id, ""); } template -inline T Response::get_header_value(const char *key, size_t id) const { - return detail::get_header_value(headers, key, id, 0); +inline T Response::get_header_value(const char* key, size_t id) const +{ + return detail::get_header_value(headers, key, id, 0); } -inline size_t Response::get_header_value_count(const char *key) const { - auto r = headers.equal_range(key); - return static_cast(std::distance(r.first, r.second)); +inline size_t Response::get_header_value_count(const char* key) const +{ + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); } -inline void Response::set_header(const char *key, const char *val) { - if (!detail::has_crlf(key) && !detail::has_crlf(val)) { - headers.emplace(key, val); - } +inline void Response::set_header(const char* key, const char* val) +{ + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } } -inline void Response::set_header(const char *key, const std::string &val) { - if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { - headers.emplace(key, val); - } +inline void Response::set_header(const char* key, const std::string& val) +{ + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } } -inline void Response::set_redirect(const char *url, int stat) { - if (!detail::has_crlf(url)) { - set_header("Location", url); - if (300 <= stat && stat < 400) { - this->status = stat; - } else { - this->status = 302; +inline void Response::set_redirect(const char* url, int stat) +{ + if (!detail::has_crlf(url)) { + set_header("Location", url); + if (300 <= stat && stat < 400) { + this->status = stat; + } else { + this->status = 302; + } } - } } -inline void Response::set_redirect(const std::string &url, int stat) { - set_redirect(url.c_str(), stat); +inline void Response::set_redirect(const std::string& url, int stat) +{ + set_redirect(url.c_str(), stat); } -inline void Response::set_content(const char *s, size_t n, - const char *content_type) { - body.assign(s, n); - set_header("Content-Type", content_type); +inline void Response::set_content(const char* s, size_t n, + const char* content_type) +{ + body.assign(s, n); + set_header("Content-Type", content_type); } -inline void Response::set_content(std::string s, const char *content_type) { - body = std::move(s); - set_header("Content-Type", content_type); +inline void Response::set_content(std::string s, const char* content_type) +{ + body = std::move(s); + set_header("Content-Type", content_type); } inline void -Response::set_content_provider(size_t in_length, const char *content_type, - ContentProvider provider, - const std::function &resource_releaser) { - assert(in_length > 0); - set_header("Content-Type", content_type); - content_length_ = in_length; - content_provider_ = std::move(provider); - content_provider_resource_releaser_ = resource_releaser; - is_chunked_content_provider = false; +Response::set_content_provider(size_t in_length, const char* content_type, + ContentProvider provider, + const std::function& resource_releaser) +{ + assert(in_length > 0); + set_header("Content-Type", content_type); + content_length_ = in_length; + content_provider_ = std::move(provider); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider = false; } inline void -Response::set_content_provider(const char *content_type, - ContentProviderWithoutLength provider, - const std::function &resource_releaser) { - set_header("Content-Type", content_type); - content_length_ = 0; - content_provider_ = detail::ContentProviderAdapter(std::move(provider)); - content_provider_resource_releaser_ = resource_releaser; - is_chunked_content_provider = false; +Response::set_content_provider(const char* content_type, + ContentProviderWithoutLength provider, + const std::function& resource_releaser) +{ + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider = false; } inline void Response::set_chunked_content_provider( - const char *content_type, ContentProviderWithoutLength provider, - const std::function &resource_releaser) { - set_header("Content-Type", content_type); - content_length_ = 0; - content_provider_ = detail::ContentProviderAdapter(std::move(provider)); - content_provider_resource_releaser_ = resource_releaser; - is_chunked_content_provider = true; + const char* content_type, ContentProviderWithoutLength provider, + const std::function& resource_releaser) +{ + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider = true; } // Rstream implementation -inline ssize_t Stream::write(const char *ptr) { - return write(ptr, strlen(ptr)); +inline ssize_t Stream::write(const char* ptr) +{ + return write(ptr, strlen(ptr)); } -inline ssize_t Stream::write(const std::string &s) { - return write(s.data(), s.size()); +inline ssize_t Stream::write(const std::string& s) +{ + return write(s.data(), s.size()); } template -inline ssize_t Stream::write_format(const char *fmt, const Args &... args) { - const auto bufsiz = 2048; - std::array buf; +inline ssize_t Stream::write_format(const char* fmt, const Args&... args) +{ + const auto bufsiz = 2048; + std::array buf; #if defined(_MSC_VER) && _MSC_VER < 1900 - auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...); + auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...); #else - auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); #endif - if (sn <= 0) { return sn; } + if (sn <= 0) { + return sn; + } - auto n = static_cast(sn); + auto n = static_cast(sn); - if (n >= buf.size() - 1) { - std::vector glowable_buf(buf.size()); + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); - while (n >= glowable_buf.size() - 1) { - glowable_buf.resize(glowable_buf.size() * 2); + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); #if defined(_MSC_VER) && _MSC_VER < 1900 - n = static_cast(_snprintf_s(&glowable_buf[0], glowable_buf.size(), - glowable_buf.size() - 1, fmt, - args...)); + n = static_cast(_snprintf_s(&glowable_buf[0], glowable_buf.size(), + glowable_buf.size() - 1, fmt, + args...)); #else - n = static_cast( - snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); + n = static_cast( + snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); #endif + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); } - return write(&glowable_buf[0], n); - } else { - return write(buf.data(), n); - } } namespace detail { -// Socket stream implementation -inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, - time_t read_timeout_usec, - time_t write_timeout_sec, - time_t write_timeout_usec) - : sock_(sock), read_timeout_sec_(read_timeout_sec), - read_timeout_usec_(read_timeout_usec), - write_timeout_sec_(write_timeout_sec), - write_timeout_usec_(write_timeout_usec) {} + // Socket stream implementation + inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock) + , read_timeout_sec_(read_timeout_sec) + , read_timeout_usec_(read_timeout_usec) + , write_timeout_sec_(write_timeout_sec) + , write_timeout_usec_(write_timeout_usec) + { + } -inline SocketStream::~SocketStream() {} + inline SocketStream::~SocketStream() { } -inline bool SocketStream::is_readable() const { - return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; -} + inline bool SocketStream::is_readable() const + { + return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; + } -inline bool SocketStream::is_writable() const { - return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; -} + inline bool SocketStream::is_writable() const + { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; + } -inline ssize_t SocketStream::read(char *ptr, size_t size) { - if (!is_readable()) { return -1; } + inline ssize_t SocketStream::read(char* ptr, size_t size) + { + if (!is_readable()) { + return -1; + } #ifdef _WIN32 - if (size > static_cast((std::numeric_limits::max)())) { - return -1; - } - return recv(sock_, ptr, static_cast(size), 0); + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return recv(sock_, ptr, static_cast(size), 0); #else - return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); }); + return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); }); #endif -} + } -inline ssize_t SocketStream::write(const char *ptr, size_t size) { - if (!is_writable()) { return -1; } + inline ssize_t SocketStream::write(const char* ptr, size_t size) + { + if (!is_writable()) { + return -1; + } #ifdef _WIN32 - if (size > static_cast((std::numeric_limits::max)())) { - return -1; - } - return send(sock_, ptr, static_cast(size), 0); + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return send(sock_, ptr, static_cast(size), 0); #else - return handle_EINTR([&]() { return send(sock_, ptr, size, 0); }); + return handle_EINTR([&]() { return send(sock_, ptr, size, 0); }); #endif -} + } -inline void SocketStream::get_remote_ip_and_port(std::string &ip, - int &port) const { - return detail::get_remote_ip_and_port(sock_, ip, port); -} + inline void SocketStream::get_remote_ip_and_port(std::string& ip, + int& port) const + { + return detail::get_remote_ip_and_port(sock_, ip, port); + } -// Buffer stream implementation -inline bool BufferStream::is_readable() const { return true; } + // Buffer stream implementation + inline bool BufferStream::is_readable() const { return true; } -inline bool BufferStream::is_writable() const { return true; } + inline bool BufferStream::is_writable() const { return true; } -inline ssize_t BufferStream::read(char *ptr, size_t size) { + inline ssize_t BufferStream::read(char* ptr, size_t size) + { #if defined(_MSC_VER) && _MSC_VER <= 1900 - auto len_read = buffer._Copy_s(ptr, size, size, position); + auto len_read = buffer._Copy_s(ptr, size, size, position); #else - auto len_read = buffer.copy(ptr, size, position); + auto len_read = buffer.copy(ptr, size, position); #endif - position += static_cast(len_read); - return static_cast(len_read); -} + position += static_cast(len_read); + return static_cast(len_read); + } -inline ssize_t BufferStream::write(const char *ptr, size_t size) { - buffer.append(ptr, size); - return static_cast(size); -} + inline ssize_t BufferStream::write(const char* ptr, size_t size) + { + buffer.append(ptr, size); + return static_cast(size); + } -inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, - int & /*port*/) const {} + inline void BufferStream::get_remote_ip_and_port(std::string& /*ip*/, + int& /*port*/) const { } -inline const std::string &BufferStream::get_buffer() const { return buffer; } + inline const std::string& BufferStream::get_buffer() const { return buffer; } } // namespace detail // HTTP server implementation inline Server::Server() : new_task_queue( - [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }), - svr_sock_(INVALID_SOCKET), is_running_(false) { + [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) + , svr_sock_(INVALID_SOCKET) + , is_running_(false) +{ #ifdef __linux__ - signal(SIGPIPE, SIG_IGN); + signal(SIGPIPE, SIG_IGN); #endif } -inline Server::~Server() {} +inline Server::~Server() { } -inline Server &Server::Get(const char *pattern, Handler handler) { - get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Get(const char* pattern, Handler handler) +{ + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Post(const char *pattern, Handler handler) { - post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Post(const char* pattern, Handler handler) +{ + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Post(const char *pattern, - HandlerWithContentReader handler) { - post_handlers_for_content_reader_.push_back( - std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Post(const char* pattern, + HandlerWithContentReader handler) +{ + post_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Put(const char *pattern, Handler handler) { - put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Put(const char* pattern, Handler handler) +{ + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Put(const char *pattern, - HandlerWithContentReader handler) { - put_handlers_for_content_reader_.push_back( - std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Put(const char* pattern, + HandlerWithContentReader handler) +{ + put_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Patch(const char *pattern, Handler handler) { - patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Patch(const char* pattern, Handler handler) +{ + patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Patch(const char *pattern, - HandlerWithContentReader handler) { - patch_handlers_for_content_reader_.push_back( - std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Patch(const char* pattern, + HandlerWithContentReader handler) +{ + patch_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Delete(const char *pattern, Handler handler) { - delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Delete(const char* pattern, Handler handler) +{ + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Delete(const char *pattern, - HandlerWithContentReader handler) { - delete_handlers_for_content_reader_.push_back( - std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Delete(const char* pattern, + HandlerWithContentReader handler) +{ + delete_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), handler)); + return *this; } -inline Server &Server::Options(const char *pattern, Handler handler) { - options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); - return *this; +inline Server& Server::Options(const char* pattern, Handler handler) +{ + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; } -inline bool Server::set_base_dir(const char *dir, const char *mount_point) { - return set_mount_point(mount_point, dir); +inline bool Server::set_base_dir(const char* dir, const char* mount_point) +{ + return set_mount_point(mount_point, dir); } -inline bool Server::set_mount_point(const char *mount_point, const char *dir) { - if (detail::is_dir(dir)) { - std::string mnt = mount_point ? mount_point : "/"; - if (!mnt.empty() && mnt[0] == '/') { - base_dirs_.emplace_back(mnt, dir); - return true; +inline bool Server::set_mount_point(const char* mount_point, const char* dir) +{ + if (detail::is_dir(dir)) { + std::string mnt = mount_point ? mount_point : "/"; + if (!mnt.empty() && mnt[0] == '/') { + base_dirs_.emplace_back(mnt, dir); + return true; + } } - } - return false; + return false; } -inline bool Server::remove_mount_point(const char *mount_point) { - for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { - if (it->first == mount_point) { - base_dirs_.erase(it); - return true; +inline bool Server::remove_mount_point(const char* mount_point) +{ + for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { + if (it->first == mount_point) { + base_dirs_.erase(it); + return true; + } } - } - return false; + return false; } -inline void Server::set_file_extension_and_mimetype_mapping(const char *ext, - const char *mime) { - file_extension_and_mimetype_map_[ext] = mime; +inline void Server::set_file_extension_and_mimetype_mapping(const char* ext, + const char* mime) +{ + file_extension_and_mimetype_map_[ext] = mime; } -inline void Server::set_file_request_handler(Handler handler) { - file_request_handler_ = std::move(handler); +inline void Server::set_file_request_handler(Handler handler) +{ + file_request_handler_ = std::move(handler); } -inline void Server::set_error_handler(Handler handler) { - error_handler_ = std::move(handler); +inline void Server::set_error_handler(Handler handler) +{ + error_handler_ = std::move(handler); } inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } -inline void Server::set_socket_options(SocketOptions socket_options) { - socket_options_ = socket_options; +inline void Server::set_socket_options(SocketOptions socket_options) +{ + socket_options_ = socket_options; } inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } inline void -Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { - expect_100_continue_handler_ = std::move(handler); +Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) +{ + expect_100_continue_handler_ = std::move(handler); } -inline void Server::set_keep_alive_max_count(size_t count) { - keep_alive_max_count_ = count; +inline void Server::set_keep_alive_max_count(size_t count) +{ + keep_alive_max_count_ = count; } -inline void Server::set_keep_alive_timeout(time_t sec) { - keep_alive_timeout_sec_ = sec; +inline void Server::set_keep_alive_timeout(time_t sec) +{ + keep_alive_timeout_sec_ = sec; } -inline void Server::set_read_timeout(time_t sec, time_t usec) { - read_timeout_sec_ = sec; - read_timeout_usec_ = usec; +inline void Server::set_read_timeout(time_t sec, time_t usec) +{ + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; } -inline void Server::set_write_timeout(time_t sec, time_t usec) { - write_timeout_sec_ = sec; - write_timeout_usec_ = usec; +inline void Server::set_write_timeout(time_t sec, time_t usec) +{ + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; } -inline void Server::set_idle_interval(time_t sec, time_t usec) { - idle_interval_sec_ = sec; - idle_interval_usec_ = usec; +inline void Server::set_idle_interval(time_t sec, time_t usec) +{ + idle_interval_sec_ = sec; + idle_interval_usec_ = usec; } -inline void Server::set_payload_max_length(size_t length) { - payload_max_length_ = length; +inline void Server::set_payload_max_length(size_t length) +{ + payload_max_length_ = length; } -inline bool Server::bind_to_port(const char *host, int port, int socket_flags) { - if (bind_internal(host, port, socket_flags) < 0) return false; - return true; +inline bool Server::bind_to_port(const char* host, int port, int socket_flags) +{ + if (bind_internal(host, port, socket_flags) < 0) + return false; + return true; } -inline int Server::bind_to_any_port(const char *host, int socket_flags) { - return bind_internal(host, 0, socket_flags); +inline int Server::bind_to_any_port(const char* host, int socket_flags) +{ + return bind_internal(host, 0, socket_flags); } inline bool Server::listen_after_bind() { return listen_internal(); } -inline bool Server::listen(const char *host, int port, int socket_flags) { - return bind_to_port(host, port, socket_flags) && listen_internal(); +inline bool Server::listen(const char* host, int port, int socket_flags) +{ + return bind_to_port(host, port, socket_flags) && listen_internal(); } inline bool Server::is_running() const { return is_running_; } -inline void Server::stop() { - if (is_running_) { - assert(svr_sock_ != INVALID_SOCKET); - std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); - detail::shutdown_socket(sock); - detail::close_socket(sock); - } +inline void Server::stop() +{ + if (is_running_) { + assert(svr_sock_ != INVALID_SOCKET); + std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); + detail::shutdown_socket(sock); + detail::close_socket(sock); + } } -inline bool Server::parse_request_line(const char *s, Request &req) { - const static std::regex re( - "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " - "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n"); +inline bool Server::parse_request_line(const char* s, Request& req) +{ + const static std::regex re( + "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " + "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n"); - std::cmatch m; - if (std::regex_match(s, m, re)) { - req.version = std::string(m[5]); - req.method = std::string(m[1]); - req.target = std::string(m[2]); - req.path = detail::decode_url(m[3], false); + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[5]); + req.method = std::string(m[1]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3], false); - // Parse query text - auto len = std::distance(m[4].first, m[4].second); - if (len > 0) { detail::parse_query_text(m[4], req.params); } + // Parse query text + auto len = std::distance(m[4].first, m[4].second); + if (len > 0) { + detail::parse_query_text(m[4], req.params); + } - return true; - } + return true; + } - return false; + return false; } -inline bool Server::write_response(Stream &strm, bool close_connection, - const Request &req, Response &res) { - assert(res.status != -1); +inline bool Server::write_response(Stream& strm, bool close_connection, + const Request& req, Response& res) +{ + assert(res.status != -1); - if (400 <= res.status && error_handler_) { error_handler_(req, res); } - - detail::BufferStream bstrm; + if (400 <= res.status && error_handler_) { + error_handler_(req, res); + } - // Response line - if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, - detail::status_message(res.status))) { - return false; - } + detail::BufferStream bstrm; - // Headers - if (close_connection || req.get_header_value("Connection") == "close") { - res.set_header("Connection", "close"); - } else { - std::stringstream ss; - ss << "timeout=" << keep_alive_timeout_sec_ - << ", max=" << keep_alive_max_count_; - res.set_header("Keep-Alive", ss.str()); - } + // Response line + if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, + detail::status_message(res.status))) { + return false; + } - if (!res.has_header("Content-Type") && - (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) { - res.set_header("Content-Type", "text/plain"); - } + // Headers + if (close_connection || req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } else { + std::stringstream ss; + ss << "timeout=" << keep_alive_timeout_sec_ + << ", max=" << keep_alive_max_count_; + res.set_header("Keep-Alive", ss.str()); + } - if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { - res.set_header("Accept-Ranges", "bytes"); - } + if (!res.has_header("Content-Type") && (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) { + res.set_header("Content-Type", "text/plain"); + } - std::string content_type; - std::string boundary; + if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { + res.set_header("Accept-Ranges", "bytes"); + } - if (req.ranges.size() > 1) { - boundary = detail::make_multipart_data_boundary(); + std::string content_type; + std::string boundary; - auto it = res.headers.find("Content-Type"); - if (it != res.headers.end()) { - content_type = it->second; - res.headers.erase(it); - } + if (req.ranges.size() > 1) { + boundary = detail::make_multipart_data_boundary(); - res.headers.emplace("Content-Type", - "multipart/byteranges; boundary=" + boundary); - } + auto it = res.headers.find("Content-Type"); + if (it != res.headers.end()) { + content_type = it->second; + res.headers.erase(it); + } - auto type = detail::encoding_type(req, res); + res.headers.emplace("Content-Type", + "multipart/byteranges; boundary=" + boundary); + } - if (res.body.empty()) { - if (res.content_length_ > 0) { - size_t length = 0; - if (req.ranges.empty()) { - length = res.content_length_; - } else if (req.ranges.size() == 1) { - auto offsets = - detail::get_range_offset_and_length(req, res.content_length_, 0); - auto offset = offsets.first; - length = offsets.second; - auto content_range = detail::make_content_range_header_field( - offset, length, res.content_length_); - res.set_header("Content-Range", content_range); - } else { - length = detail::get_multipart_ranges_data_length(req, res, boundary, - content_type); - } - res.set_header("Content-Length", std::to_string(length)); - } else { - if (res.content_provider_) { - if (res.is_chunked_content_provider) { - res.set_header("Transfer-Encoding", "chunked"); - if (type == detail::EncodingType::Gzip) { - res.set_header("Content-Encoding", "gzip"); - } else if (type == detail::EncodingType::Brotli) { - res.set_header("Content-Encoding", "br"); - } - } - } else { - res.set_header("Content-Length", "0"); - } - } - } else { - if (req.ranges.empty()) { - ; - } else if (req.ranges.size() == 1) { - auto offsets = - detail::get_range_offset_and_length(req, res.body.size(), 0); - auto offset = offsets.first; - auto length = offsets.second; - auto content_range = detail::make_content_range_header_field( - offset, length, res.body.size()); - res.set_header("Content-Range", content_range); - res.body = res.body.substr(offset, length); + auto type = detail::encoding_type(req, res); + + if (res.body.empty()) { + if (res.content_length_ > 0) { + size_t length = 0; + if (req.ranges.empty()) { + length = res.content_length_; + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + length = offsets.second; + auto content_range = detail::make_content_range_header_field( + offset, length, res.content_length_); + res.set_header("Content-Range", content_range); + } else { + length = detail::get_multipart_ranges_data_length(req, res, boundary, + content_type); + } + res.set_header("Content-Length", std::to_string(length)); + } else { + if (res.content_provider_) { + if (res.is_chunked_content_provider) { + res.set_header("Transfer-Encoding", "chunked"); + if (type == detail::EncodingType::Gzip) { + res.set_header("Content-Encoding", "gzip"); + } else if (type == detail::EncodingType::Brotli) { + res.set_header("Content-Encoding", "br"); + } + } + } else { + res.set_header("Content-Length", "0"); + } + } } else { - res.body = - detail::make_multipart_ranges_data(req, res, boundary, content_type); - } + if (req.ranges.empty()) { + ; + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.body.size(), 0); + auto offset = offsets.first; + auto length = offsets.second; + auto content_range = detail::make_content_range_header_field( + offset, length, res.body.size()); + res.set_header("Content-Range", content_range); + res.body = res.body.substr(offset, length); + } else { + res.body = detail::make_multipart_ranges_data(req, res, boundary, content_type); + } - if (type != detail::EncodingType::None) { - std::shared_ptr compressor; + if (type != detail::EncodingType::None) { + std::shared_ptr compressor; - if (type == detail::EncodingType::Gzip) { + if (type == detail::EncodingType::Gzip) { #ifdef CPPHTTPLIB_ZLIB_SUPPORT - compressor = std::make_shared(); - res.set_header("Content-Encoding", "gzip"); + compressor = std::make_shared(); + res.set_header("Content-Encoding", "gzip"); #endif - } else if (type == detail::EncodingType::Brotli) { + } else if (type == detail::EncodingType::Brotli) { #ifdef CPPHTTPLIB_BROTLI_SUPPORT - compressor = std::make_shared(); - res.set_header("Content-Encoding", "brotli"); + compressor = std::make_shared(); + res.set_header("Content-Encoding", "brotli"); #endif - } + } + + if (compressor) { + std::string compressed; - if (compressor) { - std::string compressed; + if (!compressor->compress(res.body.data(), res.body.size(), true, + [&](const char* data, size_t data_len) { + compressed.append(data, data_len); + return true; + })) { + return false; + } - if (!compressor->compress(res.body.data(), res.body.size(), true, - [&](const char *data, size_t data_len) { - compressed.append(data, data_len); - return true; - })) { - return false; + res.body.swap(compressed); + } } - res.body.swap(compressed); - } + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length); } - auto length = std::to_string(res.body.size()); - res.set_header("Content-Length", length); - } - - if (!detail::write_headers(bstrm, res, Headers())) { return false; } + if (!detail::write_headers(bstrm, res, Headers())) { + return false; + } - // Flush buffer - auto &data = bstrm.get_buffer(); - strm.write(data.data(), data.size()); + // Flush buffer + auto& data = bstrm.get_buffer(); + strm.write(data.data(), data.size()); - // Body - auto ret = true; - if (req.method != "HEAD") { - if (!res.body.empty()) { - if (!strm.write(res.body)) { ret = false; } - } else if (res.content_provider_) { - if (!write_content_with_provider(strm, req, res, boundary, - content_type)) { - ret = false; - } + // Body + auto ret = true; + if (req.method != "HEAD") { + if (!res.body.empty()) { + if (!strm.write(res.body)) { + ret = false; + } + } else if (res.content_provider_) { + if (!write_content_with_provider(strm, req, res, boundary, + content_type)) { + ret = false; + } + } } - } - // Log - if (logger_) { logger_(req, res); } + // Log + if (logger_) { + logger_(req, res); + } - return ret; + return ret; } inline bool -Server::write_content_with_provider(Stream &strm, const Request &req, - Response &res, const std::string &boundary, - const std::string &content_type) { - auto is_shutting_down = [this]() { - return this->svr_sock_ == INVALID_SOCKET; - }; - - if (res.content_length_ > 0) { - if (req.ranges.empty()) { - if (detail::write_content(strm, res.content_provider_, 0, - res.content_length_, is_shutting_down) < 0) { - return false; - } - } else if (req.ranges.size() == 1) { - auto offsets = - detail::get_range_offset_and_length(req, res.content_length_, 0); - auto offset = offsets.first; - auto length = offsets.second; - if (detail::write_content(strm, res.content_provider_, offset, length, - is_shutting_down) < 0) { - return false; - } +Server::write_content_with_provider(Stream& strm, const Request& req, + Response& res, const std::string& boundary, + const std::string& content_type) +{ + auto is_shutting_down = [this]() { + return this->svr_sock_ == INVALID_SOCKET; + }; + + if (res.content_length_ > 0) { + if (req.ranges.empty()) { + if (detail::write_content(strm, res.content_provider_, 0, + res.content_length_, is_shutting_down) + < 0) { + return false; + } + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + auto length = offsets.second; + if (detail::write_content(strm, res.content_provider_, offset, length, + is_shutting_down) + < 0) { + return false; + } + } else { + if (!detail::write_multipart_ranges_data( + strm, req, res, boundary, content_type, is_shutting_down)) { + return false; + } + } } else { - if (!detail::write_multipart_ranges_data( - strm, req, res, boundary, content_type, is_shutting_down)) { - return false; - } - } - } else { - if (res.is_chunked_content_provider) { - auto type = detail::encoding_type(req, res); + if (res.is_chunked_content_provider) { + auto type = detail::encoding_type(req, res); - std::shared_ptr compressor; - if (type == detail::EncodingType::Gzip) { + std::shared_ptr compressor; + if (type == detail::EncodingType::Gzip) { #ifdef CPPHTTPLIB_ZLIB_SUPPORT - compressor = std::make_shared(); + compressor = std::make_shared(); #endif - } else if (type == detail::EncodingType::Brotli) { + } else if (type == detail::EncodingType::Brotli) { #ifdef CPPHTTPLIB_BROTLI_SUPPORT - compressor = std::make_shared(); + compressor = std::make_shared(); #endif - } else { - compressor = std::make_shared(); - } - assert(compressor != nullptr); + } else { + compressor = std::make_shared(); + } + assert(compressor != nullptr); - if (detail::write_content_chunked(strm, res.content_provider_, - is_shutting_down, *compressor) < 0) { - return false; - } - } else { - if (detail::write_content_without_length(strm, res.content_provider_, - is_shutting_down) < 0) { - return false; - } + if (detail::write_content_chunked(strm, res.content_provider_, + is_shutting_down, *compressor) + < 0) { + return false; + } + } else { + if (detail::write_content_without_length(strm, res.content_provider_, + is_shutting_down) + < 0) { + return false; + } + } } - } - return true; + return true; } -inline bool Server::read_content(Stream &strm, Request &req, Response &res) { - MultipartFormDataMap::iterator cur; - if (read_content_core( - strm, req, res, - // Regular - [&](const char *buf, size_t n) { - if (req.body.size() + n > req.body.max_size()) { return false; } - req.body.append(buf, n); - return true; - }, - // Multipart - [&](const MultipartFormData &file) { - cur = req.files.emplace(file.name, file); - return true; - }, - [&](const char *buf, size_t n) { - auto &content = cur->second.content; - if (content.size() + n > content.max_size()) { return false; } - content.append(buf, n); - return true; - })) { - const auto &content_type = req.get_header_value("Content-Type"); - if (!content_type.find("application/x-www-form-urlencoded")) { - detail::parse_query_text(req.body, req.params); +inline bool Server::read_content(Stream& strm, Request& req, Response& res) +{ + MultipartFormDataMap::iterator cur; + if (read_content_core( + strm, req, res, + // Regular + [&](const char* buf, size_t n) { + if (req.body.size() + n > req.body.max_size()) { + return false; + } + req.body.append(buf, n); + return true; + }, + // Multipart + [&](const MultipartFormData& file) { + cur = req.files.emplace(file.name, file); + return true; + }, + [&](const char* buf, size_t n) { + auto& content = cur->second.content; + if (content.size() + n > content.max_size()) { + return false; + } + content.append(buf, n); + return true; + })) { + const auto& content_type = req.get_header_value("Content-Type"); + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } + return true; } - return true; - } - return false; + return false; } inline bool Server::read_content_with_content_receiver( - Stream &strm, Request &req, Response &res, ContentReceiver receiver, + Stream& strm, Request& req, Response& res, ContentReceiver receiver, MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver) { - return read_content_core(strm, req, res, receiver, multipart_header, - multipart_receiver); -} - -inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader mulitpart_header, - ContentReceiver multipart_receiver) { - detail::MultipartFormDataParser multipart_form_data_parser; - ContentReceiver out; + ContentReceiver multipart_receiver) +{ + return read_content_core(strm, req, res, receiver, multipart_header, + multipart_receiver); +} + +inline bool Server::read_content_core(Stream& strm, Request& req, Response& res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver) +{ + detail::MultipartFormDataParser multipart_form_data_parser; + ContentReceiver out; + + if (req.is_multipart_form_data()) { + const auto& content_type = req.get_header_value("Content-Type"); + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary)) { + res.status = 400; + return false; + } - if (req.is_multipart_form_data()) { - const auto &content_type = req.get_header_value("Content-Type"); - std::string boundary; - if (!detail::parse_multipart_boundary(content_type, boundary)) { - res.status = 400; - return false; - } - - multipart_form_data_parser.set_boundary(std::move(boundary)); - out = [&](const char *buf, size_t n) { - /* For debug - size_t pos = 0; - while (pos < n) { - auto read_size = std::min(1, n - pos); - auto ret = multipart_form_data_parser.parse( - buf + pos, read_size, multipart_receiver, mulitpart_header); - if (!ret) { return false; } - pos += read_size; - } - return true; - */ - return multipart_form_data_parser.parse(buf, n, multipart_receiver, - mulitpart_header); - }; - } else { - out = receiver; - } + multipart_form_data_parser.set_boundary(std::move(boundary)); + out = [&](const char* buf, size_t n) { + /* For debug + size_t pos = 0; + while (pos < n) { + auto read_size = std::min(1, n - pos); + auto ret = multipart_form_data_parser.parse( + buf + pos, read_size, multipart_receiver, mulitpart_header); + if (!ret) { return false; } + pos += read_size; + } + return true; + */ + return multipart_form_data_parser.parse(buf, n, multipart_receiver, + mulitpart_header); + }; + } else { + out = receiver; + } - if (req.method == "DELETE" && !req.has_header("Content-Length")) { - return true; - } + if (req.method == "DELETE" && !req.has_header("Content-Length")) { + return true; + } - if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, - out, true)) { - return false; - } + if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, + out, true)) { + return false; + } - if (req.is_multipart_form_data()) { - if (!multipart_form_data_parser.is_valid()) { - res.status = 400; - return false; + if (req.is_multipart_form_data()) { + if (!multipart_form_data_parser.is_valid()) { + res.status = 400; + return false; + } } - } - return true; + return true; } -inline bool Server::handle_file_request(Request &req, Response &res, - bool head) { - for (const auto &kv : base_dirs_) { - const auto &mount_point = kv.first; - const auto &base_dir = kv.second; - - // Prefix match - if (!req.path.compare(0, mount_point.size(), mount_point)) { - std::string sub_path = "/" + req.path.substr(mount_point.size()); - if (detail::is_valid_path(sub_path)) { - auto path = base_dir + sub_path; - if (path.back() == '/') { path += "index.html"; } - - if (detail::is_file(path)) { - detail::read_file(path, res.body); - auto type = - detail::find_content_type(path, file_extension_and_mimetype_map_); - if (type) { res.set_header("Content-Type", type); } - res.status = 200; - if (!head && file_request_handler_) { - file_request_handler_(req, res); - } - return true; +inline bool Server::handle_file_request(Request& req, Response& res, + bool head) +{ + for (const auto& kv : base_dirs_) { + const auto& mount_point = kv.first; + const auto& base_dir = kv.second; + + // Prefix match + if (!req.path.compare(0, mount_point.size(), mount_point)) { + std::string sub_path = "/" + req.path.substr(mount_point.size()); + if (detail::is_valid_path(sub_path)) { + auto path = base_dir + sub_path; + if (path.back() == '/') { + path += "index.html"; + } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::find_content_type(path, file_extension_and_mimetype_map_); + if (type) { + res.set_header("Content-Type", type); + } + res.status = 200; + if (!head && file_request_handler_) { + file_request_handler_(req, res); + } + return true; + } + } } - } } - } - return false; + return false; } inline socket_t -Server::create_server_socket(const char *host, int port, int socket_flags, - SocketOptions socket_options) const { - return detail::create_socket( - host, port, socket_flags, tcp_nodelay_, socket_options, - [](socket_t sock, struct addrinfo &ai) -> bool { - if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { - return false; - } - if (::listen(sock, 5)) { // Listen through 5 channels - return false; - } - return true; - }); +Server::create_server_socket(const char* host, int port, int socket_flags, + SocketOptions socket_options) const +{ + return detail::create_socket( + host, port, socket_flags, tcp_nodelay_, socket_options, + [](socket_t sock, struct addrinfo& ai) -> bool { + if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + return false; + } + if (::listen(sock, 5)) { // Listen through 5 channels + return false; + } + return true; + }); } -inline int Server::bind_internal(const char *host, int port, int socket_flags) { - if (!is_valid()) { return -1; } - - svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); - if (svr_sock_ == INVALID_SOCKET) { return -1; } +inline int Server::bind_internal(const char* host, int port, int socket_flags) +{ + if (!is_valid()) { + return -1; + } - if (port == 0) { - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - if (getsockname(svr_sock_, reinterpret_cast(&addr), - &addr_len) == -1) { - return -1; + svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); + if (svr_sock_ == INVALID_SOCKET) { + return -1; } - if (addr.ss_family == AF_INET) { - return ntohs(reinterpret_cast(&addr)->sin_port); - } else if (addr.ss_family == AF_INET6) { - return ntohs(reinterpret_cast(&addr)->sin6_port); + + if (port == 0) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(svr_sock_, reinterpret_cast(&addr), + &addr_len) + == -1) { + return -1; + } + if (addr.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return -1; + } } else { - return -1; + return port; } - } else { - return port; - } } -inline bool Server::listen_internal() { - auto ret = true; - is_running_ = true; +inline bool Server::listen_internal() +{ + auto ret = true; + is_running_ = true; - { - std::unique_ptr task_queue(new_task_queue()); + { + std::unique_ptr task_queue(new_task_queue()); - while (svr_sock_ != INVALID_SOCKET) { + while (svr_sock_ != INVALID_SOCKET) { #ifdef __linux__ - if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) { + if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) { #endif - auto val = detail::select_read(svr_sock_, idle_interval_sec_, - idle_interval_usec_); - if (val == 0) { // Timeout - task_queue->on_idle(); - continue; - } + auto val = detail::select_read(svr_sock_, idle_interval_sec_, + idle_interval_usec_); + if (val == 0) { // Timeout + task_queue->on_idle(); + continue; + } #ifdef __linux__ - } + } #endif - socket_t sock = accept(svr_sock_, nullptr, nullptr); - - if (sock == INVALID_SOCKET) { - if (errno == EMFILE) { - // The per-process limit of open file descriptors has been reached. - // Try to accept new connections after a short sleep. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - if (svr_sock_ != INVALID_SOCKET) { - detail::close_socket(svr_sock_); - ret = false; - } else { - ; // The server socket was closed by user. - } - break; - } + socket_t sock = accept(svr_sock_, nullptr, nullptr); + + if (sock == INVALID_SOCKET) { + if (errno == EMFILE) { + // The per-process limit of open file descriptors has been reached. + // Try to accept new connections after a short sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } #if __cplusplus > 201703L - task_queue->enqueue([=, this]() { process_and_close_socket(sock); }); + task_queue->enqueue([=, this]() { process_and_close_socket(sock); }); #else - task_queue->enqueue([=]() { process_and_close_socket(sock); }); + task_queue->enqueue([=]() { process_and_close_socket(sock); }); #endif + } + + task_queue->shutdown(); + } + + is_running_ = false; + return ret; +} + +inline bool Server::routing(Request& req, Response& res, Stream& strm) +{ + // File handler + bool is_head_request = req.method == "HEAD"; + if ((req.method == "GET" || is_head_request) && handle_file_request(req, res, is_head_request)) { + return true; + } + + if (detail::expect_content(req)) { + // Content reader handler + { + ContentReader reader( + [&](ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, receiver, + nullptr, nullptr); + }, + [&](MultipartContentHeader header, ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, nullptr, + header, receiver); + }); + + if (req.method == "POST") { + if (dispatch_request_for_content_reader( + req, res, reader, post_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PUT") { + if (dispatch_request_for_content_reader( + req, res, reader, put_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PATCH") { + if (dispatch_request_for_content_reader( + req, res, reader, patch_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "DELETE") { + if (dispatch_request_for_content_reader( + req, res, reader, delete_handlers_for_content_reader_)) { + return true; + } + } + } + + // Read content into `req.body` + if (!read_content(strm, req, res)) { + return false; + } } - task_queue->shutdown(); - } + // Regular handler + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); + } - is_running_ = false; - return ret; + res.status = 400; + return false; } -inline bool Server::routing(Request &req, Response &res, Stream &strm) { - // File handler - bool is_head_request = req.method == "HEAD"; - if ((req.method == "GET" || is_head_request) && - handle_file_request(req, res, is_head_request)) { - return true; - } +inline bool Server::dispatch_request(Request& req, Response& res, + const Handlers& handlers) +{ - if (detail::expect_content(req)) { - // Content reader handler - { - ContentReader reader( - [&](ContentReceiver receiver) { - return read_content_with_content_receiver(strm, req, res, receiver, - nullptr, nullptr); - }, - [&](MultipartContentHeader header, ContentReceiver receiver) { - return read_content_with_content_receiver(strm, req, res, nullptr, - header, receiver); - }); - - if (req.method == "POST") { - if (dispatch_request_for_content_reader( - req, res, reader, post_handlers_for_content_reader_)) { - return true; - } - } else if (req.method == "PUT") { - if (dispatch_request_for_content_reader( - req, res, reader, put_handlers_for_content_reader_)) { - return true; - } - } else if (req.method == "PATCH") { - if (dispatch_request_for_content_reader( - req, res, reader, patch_handlers_for_content_reader_)) { - return true; - } - } else if (req.method == "DELETE") { - if (dispatch_request_for_content_reader( - req, res, reader, delete_handlers_for_content_reader_)) { - return true; - } - } - } - - // Read content into `req.body` - if (!read_content(strm, req, res)) { return false; } - } - - // Regular handler - if (req.method == "GET" || req.method == "HEAD") { - return dispatch_request(req, res, get_handlers_); - } else if (req.method == "POST") { - return dispatch_request(req, res, post_handlers_); - } else if (req.method == "PUT") { - return dispatch_request(req, res, put_handlers_); - } else if (req.method == "DELETE") { - return dispatch_request(req, res, delete_handlers_); - } else if (req.method == "OPTIONS") { - return dispatch_request(req, res, options_handlers_); - } else if (req.method == "PATCH") { - return dispatch_request(req, res, patch_handlers_); - } - - res.status = 400; - return false; -} - -inline bool Server::dispatch_request(Request &req, Response &res, - const Handlers &handlers) { - - try { - for (const auto &x : handlers) { - const auto &pattern = x.first; - const auto &handler = x.second; - - if (std::regex_match(req.path, req.matches, pattern)) { - handler(req, res); - return true; - } + try { + for (const auto& x : handlers) { + const auto& pattern = x.first; + const auto& handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + } catch (const std::exception& ex) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", ex.what()); + } catch (...) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", "UNKNOWN"); } - } catch (const std::exception &ex) { - res.status = 500; - res.set_header("EXCEPTION_WHAT", ex.what()); - } catch (...) { - res.status = 500; - res.set_header("EXCEPTION_WHAT", "UNKNOWN"); - } - return false; + return false; } inline bool Server::dispatch_request_for_content_reader( - Request &req, Response &res, ContentReader content_reader, - const HandlersForContentReader &handlers) { - for (const auto &x : handlers) { - const auto &pattern = x.first; - const auto &handler = x.second; - - if (std::regex_match(req.path, req.matches, pattern)) { - handler(req, res, content_reader); - return true; + Request& req, Response& res, ContentReader content_reader, + const HandlersForContentReader& handlers) +{ + for (const auto& x : handlers) { + const auto& pattern = x.first; + const auto& handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res, content_reader); + return true; + } } - } - return false; + return false; } inline bool -Server::process_request(Stream &strm, bool close_connection, - bool &connection_closed, - const std::function &setup_request) { - std::array buf{}; +Server::process_request(Stream& strm, bool close_connection, + bool& connection_closed, + const std::function& setup_request) +{ + std::array buf {}; - detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); - // Connection has been closed on client - if (!line_reader.getline()) { return false; } + // Connection has been closed on client + if (!line_reader.getline()) { + return false; + } - Request req; - Response res; + Request req; + Response res; - res.version = "HTTP/1.1"; + res.version = "HTTP/1.1"; - // Check if the request URI doesn't exceed the limit - if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { - Headers dummy; - detail::read_headers(strm, dummy); - res.status = 414; - return write_response(strm, close_connection, req, res); - } + // Check if the request URI doesn't exceed the limit + if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = 414; + return write_response(strm, close_connection, req, res); + } - // Request line and headers - if (!parse_request_line(line_reader.ptr(), req) || - !detail::read_headers(strm, req.headers)) { - res.status = 400; - return write_response(strm, close_connection, req, res); - } + // Request line and headers + if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { + res.status = 400; + return write_response(strm, close_connection, req, res); + } - if (req.get_header_value("Connection") == "close") { - connection_closed = true; - } + if (req.get_header_value("Connection") == "close") { + connection_closed = true; + } - if (req.version == "HTTP/1.0" && - req.get_header_value("Connection") != "Keep-Alive") { - connection_closed = true; - } + if (req.version == "HTTP/1.0" && req.get_header_value("Connection") != "Keep-Alive") { + connection_closed = true; + } - strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); - req.set_header("REMOTE_ADDR", req.remote_addr); - req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); + req.set_header("REMOTE_ADDR", req.remote_addr); + req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); - if (req.has_header("Range")) { - const auto &range_header_value = req.get_header_value("Range"); - if (!detail::parse_range_header(range_header_value, req.ranges)) { - // TODO: error + if (req.has_header("Range")) { + const auto& range_header_value = req.get_header_value("Range"); + if (!detail::parse_range_header(range_header_value, req.ranges)) { + // TODO: error + } } - } - - if (setup_request) { setup_request(req); } - if (req.get_header_value("Expect") == "100-continue") { - auto status = 100; - if (expect_100_continue_handler_) { - status = expect_100_continue_handler_(req, res); + if (setup_request) { + setup_request(req); } - switch (status) { - case 100: - case 417: - strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, - detail::status_message(status)); - break; - default: return write_response(strm, close_connection, req, res); + + if (req.get_header_value("Expect") == "100-continue") { + auto status = 100; + if (expect_100_continue_handler_) { + status = expect_100_continue_handler_(req, res); + } + switch (status) { + case 100: + case 417: + strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, + detail::status_message(status)); + break; + default: + return write_response(strm, close_connection, req, res); + } } - } - // Rounting - if (routing(req, res, strm)) { - if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } - } else { - if (res.status == -1) { res.status = 404; } - } + // Rounting + if (routing(req, res, strm)) { + if (res.status == -1) { + res.status = req.ranges.empty() ? 200 : 206; + } + } else { + if (res.status == -1) { + res.status = 404; + } + } - return write_response(strm, close_connection, req, res); + return write_response(strm, close_connection, req, res); } inline bool Server::is_valid() const { return true; } -inline bool Server::process_and_close_socket(socket_t sock) { - auto ret = detail::process_server_socket( - sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, - read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, - [this](Stream &strm, bool close_connection, bool &connection_closed) { - return process_request(strm, close_connection, connection_closed, - nullptr); - }); +inline bool Server::process_and_close_socket(socket_t sock) +{ + auto ret = detail::process_server_socket( + sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, + [this](Stream& strm, bool close_connection, bool& connection_closed) { + return process_request(strm, close_connection, connection_closed, + nullptr); + }); - detail::shutdown_socket(sock); - detail::close_socket(sock); - return ret; + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; } // HTTP client implementation -inline ClientImpl::ClientImpl(const std::string &host) - : ClientImpl(host, 80, std::string(), std::string()) {} +inline ClientImpl::ClientImpl(const std::string& host) + : ClientImpl(host, 80, std::string(), std::string()) +{ +} -inline ClientImpl::ClientImpl(const std::string &host, int port) - : ClientImpl(host, port, std::string(), std::string()) {} +inline ClientImpl::ClientImpl(const std::string& host, int port) + : ClientImpl(host, port, std::string(), std::string()) +{ +} -inline ClientImpl::ClientImpl(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path) - : host_(host), port_(port), - host_and_port_(host_ + ":" + std::to_string(port_)), - client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} +inline ClientImpl::ClientImpl(const std::string& host, int port, + const std::string& client_cert_path, + const std::string& client_key_path) + : host_(host) + , port_(port) + , host_and_port_(host_ + ":" + std::to_string(port_)) + , client_cert_path_(client_cert_path) + , client_key_path_(client_key_path) +{ +} inline ClientImpl::~ClientImpl() { stop_core(); } @@ -4616,833 +5105,948 @@ inline bool ClientImpl::is_valid() const { return true; } inline Error ClientImpl::get_last_error() const { return error_; } -inline socket_t ClientImpl::create_client_socket() const { - if (!proxy_host_.empty() && proxy_port_ != -1) { +inline socket_t ClientImpl::create_client_socket() const +{ + if (!proxy_host_.empty() && proxy_port_ != -1) { + return detail::create_client_socket( + proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_, + connection_timeout_sec_, connection_timeout_usec_, interface_, error_); + } return detail::create_client_socket( - proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_, + host_.c_str(), port_, tcp_nodelay_, socket_options_, connection_timeout_sec_, connection_timeout_usec_, interface_, error_); - } - return detail::create_client_socket( - host_.c_str(), port_, tcp_nodelay_, socket_options_, - connection_timeout_sec_, connection_timeout_usec_, interface_, error_); } -inline bool ClientImpl::create_and_connect_socket(Socket &socket) { - auto sock = create_client_socket(); - if (sock == INVALID_SOCKET) { return false; } - socket.sock = sock; - return true; +inline bool ClientImpl::create_and_connect_socket(Socket& socket) +{ + auto sock = create_client_socket(); + if (sock == INVALID_SOCKET) { + return false; + } + socket.sock = sock; + return true; } -inline void ClientImpl::close_socket(Socket &socket, - bool /*process_socket_ret*/) { - detail::close_socket(socket.sock); - socket_.sock = INVALID_SOCKET; +inline void ClientImpl::close_socket(Socket& socket, + bool /*process_socket_ret*/) +{ + detail::close_socket(socket.sock); + socket_.sock = INVALID_SOCKET; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - socket_.ssl = nullptr; + socket_.ssl = nullptr; #endif } -inline bool ClientImpl::read_response_line(Stream &strm, Response &res) { - std::array buf; - - detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); - - if (!line_reader.getline()) { return false; } +inline bool ClientImpl::read_response_line(Stream& strm, Response& res) +{ + std::array buf; - const static std::regex re("(HTTP/1\\.[01]) (\\d+) (.*?)\r\n"); + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); - std::cmatch m; - if (!std::regex_match(line_reader.ptr(), m, re)) { return false; } - res.version = std::string(m[1]); - res.status = std::stoi(std::string(m[2])); - res.reason = std::string(m[3]); + if (!line_reader.getline()) { + return false; + } - // Ignore '100 Continue' - while (res.status == 100) { - if (!line_reader.getline()) { return false; } // CRLF - if (!line_reader.getline()) { return false; } // next response line + const static std::regex re("(HTTP/1\\.[01]) (\\d+) (.*?)\r\n"); - if (!std::regex_match(line_reader.ptr(), m, re)) { return false; } + std::cmatch m; + if (!std::regex_match(line_reader.ptr(), m, re)) { + return false; + } res.version = std::string(m[1]); res.status = std::stoi(std::string(m[2])); res.reason = std::string(m[3]); - } - return true; + // Ignore '100 Continue' + while (res.status == 100) { + if (!line_reader.getline()) { + return false; + } // CRLF + if (!line_reader.getline()) { + return false; + } // next response line + + if (!std::regex_match(line_reader.ptr(), m, re)) { + return false; + } + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); + } + + return true; } -inline bool ClientImpl::send(const Request &req, Response &res) { - std::lock_guard request_mutex_guard(request_mutex_); +inline bool ClientImpl::send(const Request& req, Response& res) +{ + std::lock_guard request_mutex_guard(request_mutex_); - { - std::lock_guard guard(socket_mutex_); + { + std::lock_guard guard(socket_mutex_); - auto is_alive = false; - if (socket_.is_open()) { - is_alive = detail::select_write(socket_.sock, 0, 0) > 0; - if (!is_alive) { close_socket(socket_, false); } - } + auto is_alive = false; + if (socket_.is_open()) { + is_alive = detail::select_write(socket_.sock, 0, 0) > 0; + if (!is_alive) { + close_socket(socket_, false); + } + } - if (!is_alive) { - if (!create_and_connect_socket(socket_)) { return false; } + if (!is_alive) { + if (!create_and_connect_socket(socket_)) { + return false; + } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - // TODO: refactoring - if (is_ssl()) { - auto &scli = static_cast(*this); - if (!proxy_host_.empty() && proxy_port_ != -1) { - bool success = false; - if (!scli.connect_with_proxy(socket_, res, success)) { - return success; - } - } - - if (!scli.initialize_ssl(socket_)) { return false; } - } + // TODO: refactoring + if (is_ssl()) { + auto& scli = static_cast(*this); + if (!proxy_host_.empty() && proxy_port_ != -1) { + bool success = false; + if (!scli.connect_with_proxy(socket_, res, success)) { + return success; + } + } + + if (!scli.initialize_ssl(socket_)) { + return false; + } + } #endif + } } - } - auto close_connection = !keep_alive_; + auto close_connection = !keep_alive_; - auto ret = process_socket(socket_, [&](Stream &strm) { - return handle_request(strm, req, res, close_connection); - }); + auto ret = process_socket(socket_, [&](Stream& strm) { + return handle_request(strm, req, res, close_connection); + }); - if (close_connection || !ret) { stop_core(); } + if (close_connection || !ret) { + stop_core(); + } - if (!ret) { - if (error_ == Error::Success) { error_ = Error::Unknown; } - } + if (!ret) { + if (error_ == Error::Success) { + error_ = Error::Unknown; + } + } - return ret; + return ret; } -inline bool ClientImpl::handle_request(Stream &strm, const Request &req, - Response &res, bool close_connection) { - if (req.path.empty()) { - error_ = Error::Connection; - return false; - } +inline bool ClientImpl::handle_request(Stream& strm, const Request& req, + Response& res, bool close_connection) +{ + if (req.path.empty()) { + error_ = Error::Connection; + return false; + } - bool ret; + bool ret; - if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) { - auto req2 = req; - req2.path = "http://" + host_and_port_ + req.path; - ret = process_request(strm, req2, res, close_connection); - } else { - ret = process_request(strm, req, res, close_connection); - } + if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) { + auto req2 = req; + req2.path = "http://" + host_and_port_ + req.path; + ret = process_request(strm, req2, res, close_connection); + } else { + ret = process_request(strm, req, res, close_connection); + } - if (!ret) { return false; } + if (!ret) { + return false; + } - if (300 < res.status && res.status < 400 && follow_location_) { - ret = redirect(req, res); - } + if (300 < res.status && res.status < 400 && follow_location_) { + ret = redirect(req, res); + } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - if ((res.status == 401 || res.status == 407) && - req.authorization_count_ < 5) { - auto is_proxy = res.status == 407; - const auto &username = - is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; - const auto &password = - is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; - - if (!username.empty() && !password.empty()) { - std::map auth; - if (detail::parse_www_authenticate(res, auth, is_proxy)) { - Request new_req = req; - new_req.authorization_count_ += 1; - auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; - new_req.headers.erase(key); - new_req.headers.insert(detail::make_digest_authentication_header( - req, auth, new_req.authorization_count_, detail::random_string(10), - username, password, is_proxy)); - - Response new_res; - - ret = send(new_req, new_res); - if (ret) { res = new_res; } - } + if ((res.status == 401 || res.status == 407) && req.authorization_count_ < 5) { + auto is_proxy = res.status == 407; + const auto& username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; + const auto& password = is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; + + if (!username.empty() && !password.empty()) { + std::map auth; + if (detail::parse_www_authenticate(res, auth, is_proxy)) { + Request new_req = req; + new_req.authorization_count_ += 1; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + new_req.headers.erase(key); + new_req.headers.insert(detail::make_digest_authentication_header( + req, auth, new_req.authorization_count_, detail::random_string(10), + username, password, is_proxy)); + + Response new_res; + + ret = send(new_req, new_res); + if (ret) { + res = new_res; + } + } + } } - } #endif - return ret; + return ret; } -inline bool ClientImpl::redirect(const Request &req, Response &res) { - if (req.redirect_count == 0) { - error_ = Error::ExceedRedirectCount; - return false; - } +inline bool ClientImpl::redirect(const Request& req, Response& res) +{ + if (req.redirect_count == 0) { + error_ = Error::ExceedRedirectCount; + return false; + } - auto location = detail::decode_url(res.get_header_value("location"), true); - if (location.empty()) { return false; } + auto location = detail::decode_url(res.get_header_value("location"), true); + if (location.empty()) { + return false; + } - const static std::regex re( - R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); + const static std::regex re( + R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); - std::smatch m; - if (!std::regex_match(location, m, re)) { return false; } + std::smatch m; + if (!std::regex_match(location, m, re)) { + return false; + } - auto scheme = is_ssl() ? "https" : "http"; + auto scheme = is_ssl() ? "https" : "http"; - auto next_scheme = m[1].str(); - auto next_host = m[2].str(); - auto port_str = m[3].str(); - auto next_path = m[4].str(); + auto next_scheme = m[1].str(); + auto next_host = m[2].str(); + auto port_str = m[3].str(); + auto next_path = m[4].str(); - auto next_port = port_; - if (!port_str.empty()) { - next_port = std::stoi(port_str); - } else if (!next_scheme.empty()) { - next_port = next_scheme == "https" ? 443 : 80; - } + auto next_port = port_; + if (!port_str.empty()) { + next_port = std::stoi(port_str); + } else if (!next_scheme.empty()) { + next_port = next_scheme == "https" ? 443 : 80; + } - if (next_scheme.empty()) { next_scheme = scheme; } - if (next_host.empty()) { next_host = host_; } - if (next_path.empty()) { next_path = "/"; } + if (next_scheme.empty()) { + next_scheme = scheme; + } + if (next_host.empty()) { + next_host = host_; + } + if (next_path.empty()) { + next_path = "/"; + } - if (next_scheme == scheme && next_host == host_ && next_port == port_) { - return detail::redirect(*this, req, res, next_path); - } else { - if (next_scheme == "https") { + if (next_scheme == scheme && next_host == host_ && next_port == port_) { + return detail::redirect(*this, req, res, next_path); + } else { + if (next_scheme == "https") { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSLClient cli(next_host.c_str(), next_port); - cli.copy_settings(*this); - auto ret = detail::redirect(cli, req, res, next_path); - if (!ret) { error_ = cli.get_last_error(); } - return ret; + SSLClient cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + auto ret = detail::redirect(cli, req, res, next_path); + if (!ret) { + error_ = cli.get_last_error(); + } + return ret; #else - return false; + return false; #endif - } else { - ClientImpl cli(next_host.c_str(), next_port); - cli.copy_settings(*this); - auto ret = detail::redirect(cli, req, res, next_path); - if (!ret) { error_ = cli.get_last_error(); } - return ret; + } else { + ClientImpl cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + auto ret = detail::redirect(cli, req, res, next_path); + if (!ret) { + error_ = cli.get_last_error(); + } + return ret; + } } - } } -inline bool ClientImpl::write_request(Stream &strm, const Request &req, - bool close_connection) { - detail::BufferStream bstrm; - - // Request line - const auto &path = detail::encode_url(req.path); +inline bool ClientImpl::write_request(Stream& strm, const Request& req, + bool close_connection) +{ + detail::BufferStream bstrm; - bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + // Request line + const auto& path = detail::encode_url(req.path); - // Additonal headers - Headers headers; - if (close_connection) { headers.emplace("Connection", "close"); } + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); - if (!req.has_header("Host")) { - if (is_ssl()) { - if (port_ == 443) { - headers.emplace("Host", host_); - } else { - headers.emplace("Host", host_and_port_); - } - } else { - if (port_ == 80) { - headers.emplace("Host", host_); - } else { - headers.emplace("Host", host_and_port_); - } + // Additonal headers + Headers headers; + if (close_connection) { + headers.emplace("Connection", "close"); } - } - - if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); } - if (!req.has_header("User-Agent")) { - headers.emplace("User-Agent", "cpp-httplib/0.7"); - } - - if (req.body.empty()) { - if (req.content_provider) { - auto length = std::to_string(req.content_length); - headers.emplace("Content-Length", length); - } else { - headers.emplace("Content-Length", "0"); - } - } else { - if (!req.has_header("Content-Type")) { - headers.emplace("Content-Type", "text/plain"); + if (!req.has_header("Host")) { + if (is_ssl()) { + if (port_ == 443) { + headers.emplace("Host", host_); + } else { + headers.emplace("Host", host_and_port_); + } + } else { + if (port_ == 80) { + headers.emplace("Host", host_); + } else { + headers.emplace("Host", host_and_port_); + } + } } - if (!req.has_header("Content-Length")) { - auto length = std::to_string(req.body.size()); - headers.emplace("Content-Length", length); + if (!req.has_header("Accept")) { + headers.emplace("Accept", "*/*"); } - } - if (!basic_auth_password_.empty()) { - headers.insert(make_basic_authentication_header( - basic_auth_username_, basic_auth_password_, false)); - } + if (!req.has_header("User-Agent")) { + headers.emplace("User-Agent", "cpp-httplib/0.7"); + } - if (!proxy_basic_auth_username_.empty() && - !proxy_basic_auth_password_.empty()) { - headers.insert(make_basic_authentication_header( - proxy_basic_auth_username_, proxy_basic_auth_password_, true)); - } + if (req.body.empty()) { + if (req.content_provider) { + auto length = std::to_string(req.content_length); + headers.emplace("Content-Length", length); + } else { + headers.emplace("Content-Length", "0"); + } + } else { + if (!req.has_header("Content-Type")) { + headers.emplace("Content-Type", "text/plain"); + } - if (!bearer_token_auth_token_.empty()) { - headers.insert(make_bearer_token_authentication_header( - bearer_token_auth_token_, false)); - } + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.body.size()); + headers.emplace("Content-Length", length); + } + } - if (!proxy_bearer_token_auth_token_.empty()) { - headers.insert(make_bearer_token_authentication_header( - proxy_bearer_token_auth_token_, true)); - } + if (!basic_auth_password_.empty()) { + headers.insert(make_basic_authentication_header( + basic_auth_username_, basic_auth_password_, false)); + } - detail::write_headers(bstrm, req, headers); + if (!proxy_basic_auth_username_.empty() && !proxy_basic_auth_password_.empty()) { + headers.insert(make_basic_authentication_header( + proxy_basic_auth_username_, proxy_basic_auth_password_, true)); + } - // Flush buffer - auto &data = bstrm.get_buffer(); - if (!detail::write_data(strm, data.data(), data.size())) { - error_ = Error::Write; - return false; - } + if (!bearer_token_auth_token_.empty()) { + headers.insert(make_bearer_token_authentication_header( + bearer_token_auth_token_, false)); + } - // Body - if (req.body.empty()) { - if (req.content_provider) { - size_t offset = 0; - size_t end_offset = req.content_length; + if (!proxy_bearer_token_auth_token_.empty()) { + headers.insert(make_bearer_token_authentication_header( + proxy_bearer_token_auth_token_, true)); + } - bool ok = true; + detail::write_headers(bstrm, req, headers); - DataSink data_sink; - data_sink.write = [&](const char *d, size_t l) { - if (ok) { - if (detail::write_data(strm, d, l)) { - offset += l; - } else { - ok = false; - } - } - }; - data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + // Flush buffer + auto& data = bstrm.get_buffer(); + if (!detail::write_data(strm, data.data(), data.size())) { + error_ = Error::Write; + return false; + } - while (offset < end_offset) { - if (!req.content_provider(offset, end_offset - offset, data_sink)) { - error_ = Error::Canceled; - return false; - } - if (!ok) { - error_ = Error::Write; - return false; + // Body + if (req.body.empty()) { + if (req.content_provider) { + size_t offset = 0; + size_t end_offset = req.content_length; + + bool ok = true; + + DataSink data_sink; + data_sink.write = [&](const char* d, size_t l) { + if (ok) { + if (detail::write_data(strm, d, l)) { + offset += l; + } else { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (offset < end_offset) { + if (!req.content_provider(offset, end_offset - offset, data_sink)) { + error_ = Error::Canceled; + return false; + } + if (!ok) { + error_ = Error::Write; + return false; + } + } } - } + } else { + return detail::write_data(strm, req.body.data(), req.body.size()); } - } else { - return detail::write_data(strm, req.body.data(), req.body.size()); - } - return true; + return true; } inline std::shared_ptr ClientImpl::send_with_content_provider( - const char *method, const char *path, const Headers &headers, - const std::string &body, size_t content_length, - ContentProvider content_provider, const char *content_type) { - - Request req; - req.method = method; - req.headers = default_headers_; - req.headers.insert(headers.begin(), headers.end()); - req.path = path; - - if (content_type) { req.headers.emplace("Content-Type", content_type); } + const char* method, const char* path, const Headers& headers, + const std::string& body, size_t content_length, + ContentProvider content_provider, const char* content_type) +{ + + Request req; + req.method = method; + req.headers = default_headers_; + req.headers.insert(headers.begin(), headers.end()); + req.path = path; + + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } #ifdef CPPHTTPLIB_ZLIB_SUPPORT - if (compress_) { - detail::gzip_compressor compressor; - - if (content_provider) { - auto ok = true; - size_t offset = 0; - - DataSink data_sink; - data_sink.write = [&](const char *data, size_t data_len) { - if (ok) { - auto last = offset + data_len == content_length; - - auto ret = compressor.compress( - data, data_len, last, [&](const char *data, size_t data_len) { - req.body.append(data, data_len); - return true; - }); - - if (ret) { - offset += data_len; - } else { - ok = false; - } - } - }; - data_sink.is_writable = [&](void) { return ok && true; }; - - while (ok && offset < content_length) { - if (!content_provider(offset, content_length - offset, data_sink)) { - error_ = Error::Canceled; - return nullptr; + if (compress_) { + detail::gzip_compressor compressor; + + if (content_provider) { + auto ok = true; + size_t offset = 0; + + DataSink data_sink; + data_sink.write = [&](const char* data, size_t data_len) { + if (ok) { + auto last = offset + data_len == content_length; + + auto ret = compressor.compress( + data, data_len, last, [&](const char* data, size_t data_len) { + req.body.append(data, data_len); + return true; + }); + + if (ret) { + offset += data_len; + } else { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && true; }; + + while (ok && offset < content_length) { + if (!content_provider(offset, content_length - offset, data_sink)) { + error_ = Error::Canceled; + return nullptr; + } + } + } else { + if (!compressor.compress(body.data(), body.size(), true, + [&](const char* data, size_t data_len) { + req.body.append(data, data_len); + return true; + })) { + return nullptr; + } } - } - } else { - if (!compressor.compress(body.data(), body.size(), true, - [&](const char *data, size_t data_len) { - req.body.append(data, data_len); - return true; - })) { - return nullptr; - } - } - req.headers.emplace("Content-Encoding", "gzip"); - } else + req.headers.emplace("Content-Encoding", "gzip"); + } else #endif - { - if (content_provider) { - req.content_length = content_length; - req.content_provider = content_provider; - } else { - req.body = body; + { + if (content_provider) { + req.content_length = content_length; + req.content_provider = content_provider; + } else { + req.body = body; + } } - } - auto res = std::make_shared(); + auto res = std::make_shared(); - return send(req, *res) ? res : nullptr; + return send(req, *res) ? res : nullptr; } -inline bool ClientImpl::process_request(Stream &strm, const Request &req, - Response &res, bool close_connection) { - // Send request - if (!write_request(strm, req, close_connection)) { return false; } +inline bool ClientImpl::process_request(Stream& strm, const Request& req, + Response& res, bool close_connection) +{ + // Send request + if (!write_request(strm, req, close_connection)) { + return false; + } - // Receive response and headers - if (!read_response_line(strm, res) || - !detail::read_headers(strm, res.headers)) { - error_ = Error::Read; - return false; - } - - if (req.response_handler) { - if (!req.response_handler(res)) { - error_ = Error::Canceled; - return false; - } - } - - // Body - if (req.method != "HEAD" && req.method != "CONNECT") { - auto out = - req.content_receiver - ? static_cast([&](const char *buf, size_t n) { - auto ret = req.content_receiver(buf, n); - if (!ret) { error_ = Error::Canceled; } - return ret; + // Receive response and headers + if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) { + error_ = Error::Read; + return false; + } + + if (req.response_handler) { + if (!req.response_handler(res)) { + error_ = Error::Canceled; + return false; + } + } + + // Body + if (req.method != "HEAD" && req.method != "CONNECT") { + auto out = req.content_receiver + ? static_cast([&](const char* buf, size_t n) { + auto ret = req.content_receiver(buf, n); + if (!ret) { + error_ = Error::Canceled; + } + return ret; }) - : static_cast([&](const char *buf, size_t n) { - if (res.body.size() + n > res.body.max_size()) { return false; } - res.body.append(buf, n); - return true; + : static_cast([&](const char* buf, size_t n) { + if (res.body.size() + n > res.body.max_size()) { + return false; + } + res.body.append(buf, n); + return true; }); - auto progress = [&](uint64_t current, uint64_t total) { - if (!req.progress) { return true; } - auto ret = req.progress(current, total); - if (!ret) { error_ = Error::Canceled; } - return ret; - }; + auto progress = [&](uint64_t current, uint64_t total) { + if (!req.progress) { + return true; + } + auto ret = req.progress(current, total); + if (!ret) { + error_ = Error::Canceled; + } + return ret; + }; - int dummy_status; - if (!detail::read_content(strm, res, (std::numeric_limits::max)(), - dummy_status, progress, out, decompress_)) { - if (error_ != Error::Canceled) { error_ = Error::Read; } - return false; + int dummy_status; + if (!detail::read_content(strm, res, (std::numeric_limits::max)(), + dummy_status, progress, out, decompress_)) { + if (error_ != Error::Canceled) { + error_ = Error::Read; + } + return false; + } } - } - if (res.get_header_value("Connection") == "close" || - (res.version == "HTTP/1.0" && res.reason != "Connection established")) { - stop_core(); - } + if (res.get_header_value("Connection") == "close" || (res.version == "HTTP/1.0" && res.reason != "Connection established")) { + stop_core(); + } - // Log - if (logger_) { logger_(req, res); } + // Log + if (logger_) { + logger_(req, res); + } - return true; + return true; } inline bool -ClientImpl::process_socket(Socket &socket, - std::function callback) { - return detail::process_client_socket(socket.sock, read_timeout_sec_, - read_timeout_usec_, write_timeout_sec_, - write_timeout_usec_, callback); +ClientImpl::process_socket(Socket& socket, + std::function callback) +{ + return detail::process_client_socket(socket.sock, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, callback); } inline bool ClientImpl::is_ssl() const { return false; } -inline Result ClientImpl::Get(const char *path) { - return Get(path, Headers(), Progress()); +inline Result ClientImpl::Get(const char* path) +{ + return Get(path, Headers(), Progress()); } -inline Result ClientImpl::Get(const char *path, Progress progress) { - return Get(path, Headers(), std::move(progress)); +inline Result ClientImpl::Get(const char* path, Progress progress) +{ + return Get(path, Headers(), std::move(progress)); } -inline Result ClientImpl::Get(const char *path, const Headers &headers) { - return Get(path, headers, Progress()); +inline Result ClientImpl::Get(const char* path, const Headers& headers) +{ + return Get(path, headers, Progress()); } -inline Result ClientImpl::Get(const char *path, const Headers &headers, - Progress progress) { - Request req; - req.method = "GET"; - req.path = path; - req.headers = default_headers_; - req.headers.insert(headers.begin(), headers.end()); - req.progress = std::move(progress); +inline Result ClientImpl::Get(const char* path, const Headers& headers, + Progress progress) +{ + Request req; + req.method = "GET"; + req.path = path; + req.headers = default_headers_; + req.headers.insert(headers.begin(), headers.end()); + req.progress = std::move(progress); - auto res = std::make_shared(); - auto ret = send(req, *res); - return Result{ret ? res : nullptr, get_last_error()}; + auto res = std::make_shared(); + auto ret = send(req, *res); + return Result { ret ? res : nullptr, get_last_error() }; } -inline Result ClientImpl::Get(const char *path, - ContentReceiver content_receiver) { - return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); +inline Result ClientImpl::Get(const char* path, + ContentReceiver content_receiver) +{ + return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); } -inline Result ClientImpl::Get(const char *path, - ContentReceiver content_receiver, - Progress progress) { - return Get(path, Headers(), nullptr, std::move(content_receiver), - std::move(progress)); +inline Result ClientImpl::Get(const char* path, + ContentReceiver content_receiver, + Progress progress) +{ + return Get(path, Headers(), nullptr, std::move(content_receiver), + std::move(progress)); } -inline Result ClientImpl::Get(const char *path, const Headers &headers, - ContentReceiver content_receiver) { - return Get(path, headers, nullptr, std::move(content_receiver), nullptr); +inline Result ClientImpl::Get(const char* path, const Headers& headers, + ContentReceiver content_receiver) +{ + return Get(path, headers, nullptr, std::move(content_receiver), nullptr); } -inline Result ClientImpl::Get(const char *path, const Headers &headers, - ContentReceiver content_receiver, - Progress progress) { - return Get(path, headers, nullptr, std::move(content_receiver), - std::move(progress)); +inline Result ClientImpl::Get(const char* path, const Headers& headers, + ContentReceiver content_receiver, + Progress progress) +{ + return Get(path, headers, nullptr, std::move(content_receiver), + std::move(progress)); } -inline Result ClientImpl::Get(const char *path, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return Get(path, Headers(), std::move(response_handler), content_receiver, - nullptr); +inline Result ClientImpl::Get(const char* path, + ResponseHandler response_handler, + ContentReceiver content_receiver) +{ + return Get(path, Headers(), std::move(response_handler), content_receiver, + nullptr); } -inline Result ClientImpl::Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return Get(path, headers, std::move(response_handler), content_receiver, - nullptr); +inline Result ClientImpl::Get(const char* path, const Headers& headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) +{ + return Get(path, headers, std::move(response_handler), content_receiver, + nullptr); } -inline Result ClientImpl::Get(const char *path, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress) { - return Get(path, Headers(), std::move(response_handler), content_receiver, - progress); +inline Result ClientImpl::Get(const char* path, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) +{ + return Get(path, Headers(), std::move(response_handler), content_receiver, + progress); } -inline Result ClientImpl::Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress) { - Request req; - req.method = "GET"; - req.path = path; - req.headers = default_headers_; - req.headers.insert(headers.begin(), headers.end()); - req.response_handler = std::move(response_handler); - req.content_receiver = std::move(content_receiver); - req.progress = std::move(progress); +inline Result ClientImpl::Get(const char* path, const Headers& headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) +{ + Request req; + req.method = "GET"; + req.path = path; + req.headers = default_headers_; + req.headers.insert(headers.begin(), headers.end()); + req.response_handler = std::move(response_handler); + req.content_receiver = std::move(content_receiver); + req.progress = std::move(progress); - auto res = std::make_shared(); - auto ret = send(req, *res); - return Result{ret ? res : nullptr, get_last_error()}; + auto res = std::make_shared(); + auto ret = send(req, *res); + return Result { ret ? res : nullptr, get_last_error() }; } -inline Result ClientImpl::Head(const char *path) { - return Head(path, Headers()); +inline Result ClientImpl::Head(const char* path) +{ + return Head(path, Headers()); } -inline Result ClientImpl::Head(const char *path, const Headers &headers) { - Request req; - req.method = "HEAD"; - req.headers = default_headers_; - req.headers.insert(headers.begin(), headers.end()); - req.path = path; +inline Result ClientImpl::Head(const char* path, const Headers& headers) +{ + Request req; + req.method = "HEAD"; + req.headers = default_headers_; + req.headers.insert(headers.begin(), headers.end()); + req.path = path; - auto res = std::make_shared(); - auto ret = send(req, *res); - return Result{ret ? res : nullptr, get_last_error()}; + auto res = std::make_shared(); + auto ret = send(req, *res); + return Result { ret ? res : nullptr, get_last_error() }; } -inline Result ClientImpl::Post(const char *path) { - return Post(path, std::string(), nullptr); +inline Result ClientImpl::Post(const char* path) +{ + return Post(path, std::string(), nullptr); } -inline Result ClientImpl::Post(const char *path, const std::string &body, - const char *content_type) { - return Post(path, Headers(), body, content_type); +inline Result ClientImpl::Post(const char* path, const std::string& body, + const char* content_type) +{ + return Post(path, Headers(), body, content_type); } -inline Result ClientImpl::Post(const char *path, const Headers &headers, - const std::string &body, - const char *content_type) { - auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr, - content_type); - return Result{ret, get_last_error()}; +inline Result ClientImpl::Post(const char* path, const Headers& headers, + const std::string& body, + const char* content_type) +{ + auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr, + content_type); + return Result { ret, get_last_error() }; } -inline Result ClientImpl::Post(const char *path, const Params ¶ms) { - return Post(path, Headers(), params); +inline Result ClientImpl::Post(const char* path, const Params& params) +{ + return Post(path, Headers(), params); } -inline Result ClientImpl::Post(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return Post(path, Headers(), content_length, content_provider, content_type); +inline Result ClientImpl::Post(const char* path, size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return Post(path, Headers(), content_length, content_provider, content_type); } -inline Result ClientImpl::Post(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { - auto ret = send_with_content_provider("POST", path, headers, std::string(), - content_length, content_provider, - content_type); - return Result{ret, get_last_error()}; +inline Result ClientImpl::Post(const char* path, const Headers& headers, + size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + auto ret = send_with_content_provider("POST", path, headers, std::string(), + content_length, content_provider, + content_type); + return Result { ret, get_last_error() }; } -inline Result ClientImpl::Post(const char *path, const Headers &headers, - const Params ¶ms) { - auto query = detail::params_to_query_str(params); - return Post(path, headers, query, "application/x-www-form-urlencoded"); +inline Result ClientImpl::Post(const char* path, const Headers& headers, + const Params& params) +{ + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded"); } -inline Result ClientImpl::Post(const char *path, - const MultipartFormDataItems &items) { - return Post(path, Headers(), items); +inline Result ClientImpl::Post(const char* path, + const MultipartFormDataItems& items) +{ + return Post(path, Headers(), items); } -inline Result ClientImpl::Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items) { - auto boundary = detail::make_multipart_data_boundary(); +inline Result ClientImpl::Post(const char* path, const Headers& headers, + const MultipartFormDataItems& items) +{ + auto boundary = detail::make_multipart_data_boundary(); - std::string body; + std::string body; - for (const auto &item : items) { - body += "--" + boundary + "\r\n"; - body += "Content-Disposition: form-data; name=\"" + item.name + "\""; - if (!item.filename.empty()) { - body += "; filename=\"" + item.filename + "\""; - } - body += "\r\n"; - if (!item.content_type.empty()) { - body += "Content-Type: " + item.content_type + "\r\n"; + for (const auto& item : items) { + body += "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + body += item.content + "\r\n"; } - body += "\r\n"; - body += item.content + "\r\n"; - } - body += "--" + boundary + "--\r\n"; + body += "--" + boundary + "--\r\n"; - std::string content_type = "multipart/form-data; boundary=" + boundary; - return Post(path, headers, body, content_type.c_str()); + std::string content_type = "multipart/form-data; boundary=" + boundary; + return Post(path, headers, body, content_type.c_str()); } -inline Result ClientImpl::Put(const char *path) { - return Put(path, std::string(), nullptr); +inline Result ClientImpl::Put(const char* path) +{ + return Put(path, std::string(), nullptr); } -inline Result ClientImpl::Put(const char *path, const std::string &body, - const char *content_type) { - return Put(path, Headers(), body, content_type); +inline Result ClientImpl::Put(const char* path, const std::string& body, + const char* content_type) +{ + return Put(path, Headers(), body, content_type); } -inline Result ClientImpl::Put(const char *path, const Headers &headers, - const std::string &body, - const char *content_type) { - auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr, - content_type); - return Result{ret, get_last_error()}; +inline Result ClientImpl::Put(const char* path, const Headers& headers, + const std::string& body, + const char* content_type) +{ + auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr, + content_type); + return Result { ret, get_last_error() }; } -inline Result ClientImpl::Put(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return Put(path, Headers(), content_length, content_provider, content_type); +inline Result ClientImpl::Put(const char* path, size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return Put(path, Headers(), content_length, content_provider, content_type); } -inline Result ClientImpl::Put(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { - auto ret = send_with_content_provider("PUT", path, headers, std::string(), - content_length, content_provider, - content_type); - return Result{ret, get_last_error()}; +inline Result ClientImpl::Put(const char* path, const Headers& headers, + size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + auto ret = send_with_content_provider("PUT", path, headers, std::string(), + content_length, content_provider, + content_type); + return Result { ret, get_last_error() }; } -inline Result ClientImpl::Put(const char *path, const Params ¶ms) { - return Put(path, Headers(), params); +inline Result ClientImpl::Put(const char* path, const Params& params) +{ + return Put(path, Headers(), params); } -inline Result ClientImpl::Put(const char *path, const Headers &headers, - const Params ¶ms) { - auto query = detail::params_to_query_str(params); - return Put(path, headers, query, "application/x-www-form-urlencoded"); +inline Result ClientImpl::Put(const char* path, const Headers& headers, + const Params& params) +{ + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded"); } -inline Result ClientImpl::Patch(const char *path, const std::string &body, - const char *content_type) { - return Patch(path, Headers(), body, content_type); +inline Result ClientImpl::Patch(const char* path, const std::string& body, + const char* content_type) +{ + return Patch(path, Headers(), body, content_type); } -inline Result ClientImpl::Patch(const char *path, const Headers &headers, - const std::string &body, - const char *content_type) { - auto ret = send_with_content_provider("PATCH", path, headers, body, 0, - nullptr, content_type); - return Result{ret, get_last_error()}; +inline Result ClientImpl::Patch(const char* path, const Headers& headers, + const std::string& body, + const char* content_type) +{ + auto ret = send_with_content_provider("PATCH", path, headers, body, 0, + nullptr, content_type); + return Result { ret, get_last_error() }; } -inline Result ClientImpl::Patch(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return Patch(path, Headers(), content_length, content_provider, content_type); +inline Result ClientImpl::Patch(const char* path, size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return Patch(path, Headers(), content_length, content_provider, content_type); } -inline Result ClientImpl::Patch(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { - auto ret = send_with_content_provider("PATCH", path, headers, std::string(), - content_length, content_provider, - content_type); - return Result{ret, get_last_error()}; +inline Result ClientImpl::Patch(const char* path, const Headers& headers, + size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + auto ret = send_with_content_provider("PATCH", path, headers, std::string(), + content_length, content_provider, + content_type); + return Result { ret, get_last_error() }; } -inline Result ClientImpl::Delete(const char *path) { - return Delete(path, Headers(), std::string(), nullptr); +inline Result ClientImpl::Delete(const char* path) +{ + return Delete(path, Headers(), std::string(), nullptr); } -inline Result ClientImpl::Delete(const char *path, const std::string &body, - const char *content_type) { - return Delete(path, Headers(), body, content_type); +inline Result ClientImpl::Delete(const char* path, const std::string& body, + const char* content_type) +{ + return Delete(path, Headers(), body, content_type); } -inline Result ClientImpl::Delete(const char *path, const Headers &headers) { - return Delete(path, headers, std::string(), nullptr); +inline Result ClientImpl::Delete(const char* path, const Headers& headers) +{ + return Delete(path, headers, std::string(), nullptr); } -inline Result ClientImpl::Delete(const char *path, const Headers &headers, - const std::string &body, - const char *content_type) { - Request req; - req.method = "DELETE"; - req.headers = default_headers_; - req.headers.insert(headers.begin(), headers.end()); - req.path = path; +inline Result ClientImpl::Delete(const char* path, const Headers& headers, + const std::string& body, + const char* content_type) +{ + Request req; + req.method = "DELETE"; + req.headers = default_headers_; + req.headers.insert(headers.begin(), headers.end()); + req.path = path; - if (content_type) { req.headers.emplace("Content-Type", content_type); } - req.body = body; + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + req.body = body; - auto res = std::make_shared(); - auto ret = send(req, *res); - return Result{ret ? res : nullptr, get_last_error()}; + auto res = std::make_shared(); + auto ret = send(req, *res); + return Result { ret ? res : nullptr, get_last_error() }; } -inline Result ClientImpl::Options(const char *path) { - return Options(path, Headers()); +inline Result ClientImpl::Options(const char* path) +{ + return Options(path, Headers()); } -inline Result ClientImpl::Options(const char *path, const Headers &headers) { - Request req; - req.method = "OPTIONS"; - req.headers = default_headers_; - req.headers.insert(headers.begin(), headers.end()); - req.path = path; +inline Result ClientImpl::Options(const char* path, const Headers& headers) +{ + Request req; + req.method = "OPTIONS"; + req.headers = default_headers_; + req.headers.insert(headers.begin(), headers.end()); + req.path = path; - auto res = std::make_shared(); - auto ret = send(req, *res); - return Result{ret ? res : nullptr, get_last_error()}; + auto res = std::make_shared(); + auto ret = send(req, *res); + return Result { ret ? res : nullptr, get_last_error() }; } -inline size_t ClientImpl::is_socket_open() const { - std::lock_guard guard(socket_mutex_); - return socket_.is_open(); +inline size_t ClientImpl::is_socket_open() const +{ + std::lock_guard guard(socket_mutex_); + return socket_.is_open(); } -inline void ClientImpl::stop() { - stop_core(); - error_ = Error::Canceled; +inline void ClientImpl::stop() +{ + stop_core(); + error_ = Error::Canceled; } -inline void ClientImpl::stop_core() { - std::lock_guard guard(socket_mutex_); - if (socket_.is_open()) { - detail::shutdown_socket(socket_.sock); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - close_socket(socket_, true); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } +inline void ClientImpl::stop_core() +{ + std::lock_guard guard(socket_mutex_); + if (socket_.is_open()) { + detail::shutdown_socket(socket_.sock); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + close_socket(socket_, true); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } } -inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { - connection_timeout_sec_ = sec; - connection_timeout_usec_ = usec; +inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) +{ + connection_timeout_sec_ = sec; + connection_timeout_usec_ = usec; } -inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) { - read_timeout_sec_ = sec; - read_timeout_usec_ = usec; +inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) +{ + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; } -inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) { - write_timeout_sec_ = sec; - write_timeout_usec_ = usec; +inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) +{ + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; } -inline void ClientImpl::set_basic_auth(const char *username, - const char *password) { - basic_auth_username_ = username; - basic_auth_password_ = password; +inline void ClientImpl::set_basic_auth(const char* username, + const char* password) +{ + basic_auth_username_ = username; + basic_auth_password_ = password; } -inline void ClientImpl::set_bearer_token_auth(const char *token) { - bearer_token_auth_token_ = token; +inline void ClientImpl::set_bearer_token_auth(const char* token) +{ + bearer_token_auth_token_ = token; } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void ClientImpl::set_digest_auth(const char *username, - const char *password) { - digest_auth_username_ = username; - digest_auth_password_ = password; +inline void ClientImpl::set_digest_auth(const char* username, + const char* password) +{ + digest_auth_username_ = username; + digest_auth_password_ = password; } #endif @@ -5450,47 +6054,54 @@ inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; } inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; } -inline void ClientImpl::set_default_headers(Headers headers) { - default_headers_ = std::move(headers); +inline void ClientImpl::set_default_headers(Headers headers) +{ + default_headers_ = std::move(headers); } inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } -inline void ClientImpl::set_socket_options(SocketOptions socket_options) { - socket_options_ = socket_options; +inline void ClientImpl::set_socket_options(SocketOptions socket_options) +{ + socket_options_ = socket_options; } inline void ClientImpl::set_compress(bool on) { compress_ = on; } inline void ClientImpl::set_decompress(bool on) { decompress_ = on; } -inline void ClientImpl::set_interface(const char *intf) { interface_ = intf; } +inline void ClientImpl::set_interface(const char* intf) { interface_ = intf; } -inline void ClientImpl::set_proxy(const char *host, int port) { - proxy_host_ = host; - proxy_port_ = port; +inline void ClientImpl::set_proxy(const char* host, int port) +{ + proxy_host_ = host; + proxy_port_ = port; } -inline void ClientImpl::set_proxy_basic_auth(const char *username, - const char *password) { - proxy_basic_auth_username_ = username; - proxy_basic_auth_password_ = password; +inline void ClientImpl::set_proxy_basic_auth(const char* username, + const char* password) +{ + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; } -inline void ClientImpl::set_proxy_bearer_token_auth(const char *token) { - proxy_bearer_token_auth_token_ = token; +inline void ClientImpl::set_proxy_bearer_token_auth(const char* token) +{ + proxy_bearer_token_auth_token_ = token; } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void ClientImpl::set_proxy_digest_auth(const char *username, - const char *password) { - proxy_digest_auth_username_ = username; - proxy_digest_auth_password_ = password; +inline void ClientImpl::set_proxy_digest_auth(const char* username, + const char* password) +{ + proxy_digest_auth_username_ = username; + proxy_digest_auth_password_ = password; } #endif -inline void ClientImpl::set_logger(Logger logger) { - logger_ = std::move(logger); +inline void ClientImpl::set_logger(Logger logger) +{ + logger_ = std::move(logger); } /* @@ -5499,957 +6110,1070 @@ inline void ClientImpl::set_logger(Logger logger) { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT namespace detail { -template -inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, - U SSL_connect_or_accept, V setup) { - SSL *ssl = nullptr; - { - std::lock_guard guard(ctx_mutex); - ssl = SSL_new(ctx); - } - - if (ssl) { - auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); - SSL_set_bio(ssl, bio, bio); - - if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { - SSL_shutdown(ssl); - { - std::lock_guard guard(ctx_mutex); - SSL_free(ssl); - } - return nullptr; + template + inline SSL* ssl_new(socket_t sock, SSL_CTX* ctx, std::mutex& ctx_mutex, + U SSL_connect_or_accept, V setup) + { + SSL* ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + ssl = SSL_new(ctx); + } + + if (ssl) { + auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + + if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + return nullptr; + } + } + + return ssl; } - } - return ssl; -} + inline void ssl_delete(std::mutex& ctx_mutex, SSL* ssl, + bool process_socket_ret) + { + if (process_socket_ret) { + SSL_shutdown(ssl); // shutdown only if not already closed by remote + } -inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, - bool process_socket_ret) { - if (process_socket_ret) { - SSL_shutdown(ssl); // shutdown only if not already closed by remote - } + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } - std::lock_guard guard(ctx_mutex); - SSL_free(ssl); -} + template + inline bool + process_server_socket_ssl(SSL* ssl, socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, + time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec, + T callback) + { + return process_server_socket_core( + sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool& connection_closed) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); + } -template -inline bool -process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count, - time_t keep_alive_timeout_sec, - time_t read_timeout_sec, time_t read_timeout_usec, - time_t write_timeout_sec, time_t write_timeout_usec, - T callback) { - return process_server_socket_core( - sock, keep_alive_max_count, keep_alive_timeout_sec, - [&](bool close_connection, bool &connection_closed) { + template + inline bool + process_client_socket_ssl(SSL* ssl, socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) + { SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, - write_timeout_sec, write_timeout_usec); - return callback(strm, close_connection, connection_closed); - }); -} - -template -inline bool -process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, - time_t read_timeout_usec, time_t write_timeout_sec, - time_t write_timeout_usec, T callback) { - SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, - write_timeout_sec, write_timeout_usec); - return callback(strm); -} + write_timeout_sec, write_timeout_usec); + return callback(strm); + } #if OPENSSL_VERSION_NUMBER < 0x10100000L -static std::shared_ptr> openSSL_locks_; + static std::shared_ptr> openSSL_locks_; -class SSLThreadLocks { -public: - SSLThreadLocks() { - openSSL_locks_ = - std::make_shared>(CRYPTO_num_locks()); - CRYPTO_set_locking_callback(locking_callback); - } + class SSLThreadLocks { + public: + SSLThreadLocks() + { + openSSL_locks_ = std::make_shared>(CRYPTO_num_locks()); + CRYPTO_set_locking_callback(locking_callback); + } - ~SSLThreadLocks() { CRYPTO_set_locking_callback(nullptr); } + ~SSLThreadLocks() { CRYPTO_set_locking_callback(nullptr); } -private: - static void locking_callback(int mode, int type, const char * /*file*/, - int /*line*/) { - auto &lk = (*openSSL_locks_)[static_cast(type)]; - if (mode & CRYPTO_LOCK) { - lk.lock(); - } else { - lk.unlock(); - } - } -}; + private: + static void locking_callback(int mode, int type, const char* /*file*/, + int /*line*/) + { + auto& lk = (*openSSL_locks_)[static_cast(type)]; + if (mode & CRYPTO_LOCK) { + lk.lock(); + } else { + lk.unlock(); + } + } + }; #endif -class SSLInit { -public: - SSLInit() { + class SSLInit { + public: + SSLInit() + { #if OPENSSL_VERSION_NUMBER < 0x1010001fL - SSL_load_error_strings(); - SSL_library_init(); + SSL_load_error_strings(); + SSL_library_init(); #else - OPENSSL_init_ssl( - OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + OPENSSL_init_ssl( + OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); #endif - } + } - ~SSLInit() { + ~SSLInit() + { #if OPENSSL_VERSION_NUMBER < 0x1010001fL - ERR_free_strings(); + ERR_free_strings(); #endif - } + } -private: + private: #if OPENSSL_VERSION_NUMBER < 0x10100000L - SSLThreadLocks thread_init_; + SSLThreadLocks thread_init_; #endif -}; + }; -// SSL socket stream implementation -inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, - time_t read_timeout_sec, - time_t read_timeout_usec, - time_t write_timeout_sec, - time_t write_timeout_usec) - : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), - read_timeout_usec_(read_timeout_usec), - write_timeout_sec_(write_timeout_sec), - write_timeout_usec_(write_timeout_usec) { - { - timeval tv; - tv.tv_sec = static_cast(read_timeout_sec); - tv.tv_usec = static_cast(read_timeout_usec); + // SSL socket stream implementation + inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL* ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock) + , ssl_(ssl) + , read_timeout_sec_(read_timeout_sec) + , read_timeout_usec_(read_timeout_usec) + , write_timeout_sec_(write_timeout_sec) + , write_timeout_usec_(write_timeout_usec) + { + { + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec); + tv.tv_usec = static_cast(read_timeout_usec); - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&tv), - sizeof(tv)); - } - { - timeval tv; - tv.tv_sec = static_cast(write_timeout_sec); - tv.tv_usec = static_cast(write_timeout_usec); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&tv), + sizeof(tv)); + } + { + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec); + tv.tv_usec = static_cast(write_timeout_usec); - setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&tv), - sizeof(tv)); - } -} + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&tv), + sizeof(tv)); + } + } -inline SSLSocketStream::~SSLSocketStream() {} + inline SSLSocketStream::~SSLSocketStream() { } -inline bool SSLSocketStream::is_readable() const { - return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; -} + inline bool SSLSocketStream::is_readable() const + { + return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; + } -inline bool SSLSocketStream::is_writable() const { - return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > - 0; -} + inline bool SSLSocketStream::is_writable() const + { + return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; + } -inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { - if (SSL_pending(ssl_) > 0 || is_readable()) { - return SSL_read(ssl_, ptr, static_cast(size)); - } - return -1; -} + inline ssize_t SSLSocketStream::read(char* ptr, size_t size) + { + if (SSL_pending(ssl_) > 0 || is_readable()) { + return SSL_read(ssl_, ptr, static_cast(size)); + } + return -1; + } -inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { - if (is_writable()) { return SSL_write(ssl_, ptr, static_cast(size)); } - return -1; -} + inline ssize_t SSLSocketStream::write(const char* ptr, size_t size) + { + if (is_writable()) { + return SSL_write(ssl_, ptr, static_cast(size)); + } + return -1; + } -inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, - int &port) const { - detail::get_remote_ip_and_port(sock_, ip, port); -} + inline void SSLSocketStream::get_remote_ip_and_port(std::string& ip, + int& port) const + { + detail::get_remote_ip_and_port(sock_, ip, port); + } -static SSLInit sslinit_; + static SSLInit sslinit_; } // namespace detail // SSL HTTP server implementation -inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, - const char *client_ca_cert_file_path, - const char *client_ca_cert_dir_path) { - ctx_ = SSL_CTX_new(SSLv23_server_method()); - - if (ctx_) { - SSL_CTX_set_options(ctx_, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - - // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); - // EC_KEY_free(ecdh); - - if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || - SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != - 1) { - SSL_CTX_free(ctx_); - ctx_ = nullptr; - } else if (client_ca_cert_file_path || client_ca_cert_dir_path) { - // if (client_ca_cert_file_path) { - // auto list = SSL_load_client_CA_file(client_ca_cert_file_path); - // SSL_CTX_set_client_CA_list(ctx_, list); - // } - - SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, - client_ca_cert_dir_path); - - SSL_CTX_set_verify( - ctx_, - SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, - nullptr); +inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path, + const char* client_ca_cert_file_path, + const char* client_ca_cert_dir_path) +{ + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + // EC_KEY_free(ecdh); + + if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_file_path || client_ca_cert_dir_path) { + // if (client_ca_cert_file_path) { + // auto list = SSL_load_client_CA_file(client_ca_cert_file_path); + // SSL_CTX_set_client_CA_list(ctx_, list); + // } + + SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, + client_ca_cert_dir_path); + + SSL_CTX_set_verify( + ctx_, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + } } - } } -inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, - X509_STORE *client_ca_cert_store) { - ctx_ = SSL_CTX_new(SSLv23_server_method()); +inline SSLServer::SSLServer(X509* cert, EVP_PKEY* private_key, + X509_STORE* client_ca_cert_store) +{ + ctx_ = SSL_CTX_new(SSLv23_server_method()); - if (ctx_) { - SSL_CTX_set_options(ctx_, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - if (SSL_CTX_use_certificate(ctx_, cert) != 1 || - SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { - SSL_CTX_free(ctx_); - ctx_ = nullptr; - } else if (client_ca_cert_store) { + if (SSL_CTX_use_certificate(ctx_, cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_store) { - SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); + SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); - SSL_CTX_set_verify( - ctx_, - SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, - nullptr); + SSL_CTX_set_verify( + ctx_, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + } } - } } -inline SSLServer::~SSLServer() { - if (ctx_) { SSL_CTX_free(ctx_); } +inline SSLServer::~SSLServer() +{ + if (ctx_) { + SSL_CTX_free(ctx_); + } } inline bool SSLServer::is_valid() const { return ctx_; } -inline bool SSLServer::process_and_close_socket(socket_t sock) { - auto ssl = detail::ssl_new(sock, ctx_, ctx_mutex_, SSL_accept, - [](SSL * /*ssl*/) { return true; }); - - if (ssl) { - auto ret = detail::process_server_socket_ssl( - ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, - read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, - write_timeout_usec_, - [this, ssl](Stream &strm, bool close_connection, - bool &connection_closed) { - return process_request(strm, close_connection, connection_closed, - [&](Request &req) { req.ssl = ssl; }); - }); - - detail::ssl_delete(ctx_mutex_, ssl, ret); - return ret; - } +inline bool SSLServer::process_and_close_socket(socket_t sock) +{ + auto ssl = detail::ssl_new(sock, ctx_, ctx_mutex_, SSL_accept, + [](SSL* /*ssl*/) { return true; }); + + if (ssl) { + auto ret = detail::process_server_socket_ssl( + ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, + [this, ssl](Stream& strm, bool close_connection, + bool& connection_closed) { + return process_request(strm, close_connection, connection_closed, + [&](Request& req) { req.ssl = ssl; }); + }); + + detail::ssl_delete(ctx_mutex_, ssl, ret); + return ret; + } - detail::close_socket(sock); - return false; + detail::close_socket(sock); + return false; } // SSL HTTP client implementation -inline SSLClient::SSLClient(const std::string &host) - : SSLClient(host, 443, std::string(), std::string()) {} +inline SSLClient::SSLClient(const std::string& host) + : SSLClient(host, 443, std::string(), std::string()) +{ +} -inline SSLClient::SSLClient(const std::string &host, int port) - : SSLClient(host, port, std::string(), std::string()) {} +inline SSLClient::SSLClient(const std::string& host, int port) + : SSLClient(host, port, std::string(), std::string()) +{ +} -inline SSLClient::SSLClient(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path) - : ClientImpl(host, port, client_cert_path, client_key_path) { - ctx_ = SSL_CTX_new(SSLv23_client_method()); +inline SSLClient::SSLClient(const std::string& host, int port, + const std::string& client_cert_path, + const std::string& client_key_path) + : ClientImpl(host, port, client_cert_path, client_key_path) +{ + ctx_ = SSL_CTX_new(SSLv23_client_method()); - detail::split(&host_[0], &host_[host_.size()], '.', - [&](const char *b, const char *e) { - host_components_.emplace_back(std::string(b, e)); - }); - if (!client_cert_path.empty() && !client_key_path.empty()) { - if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), - SSL_FILETYPE_PEM) != 1 || - SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), - SSL_FILETYPE_PEM) != 1) { - SSL_CTX_free(ctx_); - ctx_ = nullptr; + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char* b, const char* e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (!client_cert_path.empty() && !client_key_path.empty()) { + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), + SSL_FILETYPE_PEM) + != 1 + || SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), + SSL_FILETYPE_PEM) + != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } } - } } -inline SSLClient::SSLClient(const std::string &host, int port, - X509 *client_cert, EVP_PKEY *client_key) - : ClientImpl(host, port) { - ctx_ = SSL_CTX_new(SSLv23_client_method()); +inline SSLClient::SSLClient(const std::string& host, int port, + X509* client_cert, EVP_PKEY* client_key) + : ClientImpl(host, port) +{ + ctx_ = SSL_CTX_new(SSLv23_client_method()); - detail::split(&host_[0], &host_[host_.size()], '.', - [&](const char *b, const char *e) { - host_components_.emplace_back(std::string(b, e)); - }); - if (client_cert != nullptr && client_key != nullptr) { - if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || - SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { - SSL_CTX_free(ctx_); - ctx_ = nullptr; + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char* b, const char* e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (client_cert != nullptr && client_key != nullptr) { + if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } } - } } -inline SSLClient::~SSLClient() { - if (ctx_) { SSL_CTX_free(ctx_); } +inline SSLClient::~SSLClient() +{ + if (ctx_) { + SSL_CTX_free(ctx_); + } } inline bool SSLClient::is_valid() const { return ctx_; } -inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, - const char *ca_cert_dir_path) { - if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; } - if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; } +inline void SSLClient::set_ca_cert_path(const char* ca_cert_file_path, + const char* ca_cert_dir_path) +{ + if (ca_cert_file_path) { + ca_cert_file_path_ = ca_cert_file_path; + } + if (ca_cert_dir_path) { + ca_cert_dir_path_ = ca_cert_dir_path; + } } -inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { - if (ca_cert_store) { ca_cert_store_ = ca_cert_store; } +inline void SSLClient::set_ca_cert_store(X509_STORE* ca_cert_store) +{ + if (ca_cert_store) { + ca_cert_store_ = ca_cert_store; + } } -inline void SSLClient::enable_server_certificate_verification(bool enabled) { - server_certificate_verification_ = enabled; +inline void SSLClient::enable_server_certificate_verification(bool enabled) +{ + server_certificate_verification_ = enabled; } -inline long SSLClient::get_openssl_verify_result() const { - return verify_result_; +inline long SSLClient::get_openssl_verify_result() const +{ + return verify_result_; } -inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; } +inline SSL_CTX* SSLClient::ssl_context() const { return ctx_; } -inline bool SSLClient::create_and_connect_socket(Socket &socket) { - return is_valid() && ClientImpl::create_and_connect_socket(socket); +inline bool SSLClient::create_and_connect_socket(Socket& socket) +{ + return is_valid() && ClientImpl::create_and_connect_socket(socket); } -inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, - bool &success) { - success = true; - Response res2; +inline bool SSLClient::connect_with_proxy(Socket& socket, Response& res, + bool& success) +{ + success = true; + Response res2; - if (!detail::process_client_socket( - socket.sock, read_timeout_sec_, read_timeout_usec_, - write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { - Request req2; - req2.method = "CONNECT"; - req2.path = host_and_port_; - return process_request(strm, req2, res2, false); - })) { - close_socket(socket, true); - success = false; - return false; - } - - if (res2.status == 407) { - if (!proxy_digest_auth_username_.empty() && - !proxy_digest_auth_password_.empty()) { - std::map auth; - if (detail::parse_www_authenticate(res2, auth, true)) { - Response res3; - if (!detail::process_client_socket( - socket.sock, read_timeout_sec_, read_timeout_usec_, - write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { - Request req3; - req3.method = "CONNECT"; - req3.path = host_and_port_; - req3.headers.insert(detail::make_digest_authentication_header( - req3, auth, 1, detail::random_string(10), - proxy_digest_auth_username_, proxy_digest_auth_password_, - true)); - return process_request(strm, req3, res3, false); - })) { - close_socket(socket, true); - success = false; - return false; - } - } - } else { - res = res2; - return false; - } - } - - return true; -} - -inline bool SSLClient::load_certs() { - bool ret = true; - - std::call_once(initialize_cert_, [&]() { - std::lock_guard guard(ctx_mutex_); - if (!ca_cert_file_path_.empty()) { - if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), - nullptr)) { - ret = false; - } - } else if (!ca_cert_dir_path_.empty()) { - if (!SSL_CTX_load_verify_locations(ctx_, nullptr, - ca_cert_dir_path_.c_str())) { - ret = false; - } - } else if (ca_cert_store_ != nullptr) { - if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) { - SSL_CTX_set_cert_store(ctx_, ca_cert_store_); - } - } else { + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream& strm) { + Request req2; + req2.method = "CONNECT"; + req2.path = host_and_port_; + return process_request(strm, req2, res2, false); + })) { + close_socket(socket, true); + success = false; + return false; + } + + if (res2.status == 407) { + if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty()) { + std::map auth; + if (detail::parse_www_authenticate(res2, auth, true)) { + Response res3; + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream& strm) { + Request req3; + req3.method = "CONNECT"; + req3.path = host_and_port_; + req3.headers.insert(detail::make_digest_authentication_header( + req3, auth, 1, detail::random_string(10), + proxy_digest_auth_username_, proxy_digest_auth_password_, + true)); + return process_request(strm, req3, res3, false); + })) { + close_socket(socket, true); + success = false; + return false; + } + } + } else { + res = res2; + return false; + } + } + + return true; +} + +inline bool SSLClient::load_certs() +{ + bool ret = true; + + std::call_once(initialize_cert_, [&]() { + std::lock_guard guard(ctx_mutex_); + if (!ca_cert_file_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), + nullptr)) { + ret = false; + } + } else if (!ca_cert_dir_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, nullptr, + ca_cert_dir_path_.c_str())) { + ret = false; + } + } else if (ca_cert_store_ != nullptr) { + if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) { + SSL_CTX_set_cert_store(ctx_, ca_cert_store_); + } + } else { #ifdef _WIN32 - detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_)); + detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_)); #else SSL_CTX_set_default_verify_paths(ctx_); #endif - } - }); + } + }); - return ret; + return ret; } -inline bool SSLClient::initialize_ssl(Socket &socket) { - auto ssl = detail::ssl_new( - socket.sock, ctx_, ctx_mutex_, - [&](SSL *ssl) { - if (server_certificate_verification_) { - if (!load_certs()) { - error_ = Error::SSLLoadingCerts; - return false; - } - SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr); - } +inline bool SSLClient::initialize_ssl(Socket& socket) +{ + auto ssl = detail::ssl_new( + socket.sock, ctx_, ctx_mutex_, + [&](SSL* ssl) { + if (server_certificate_verification_) { + if (!load_certs()) { + error_ = Error::SSLLoadingCerts; + return false; + } + SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr); + } - if (SSL_connect(ssl) != 1) { - error_ = Error::SSLConnection; - return false; - } + if (SSL_connect(ssl) != 1) { + error_ = Error::SSLConnection; + return false; + } - if (server_certificate_verification_) { - verify_result_ = SSL_get_verify_result(ssl); + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl); - if (verify_result_ != X509_V_OK) { - error_ = Error::SSLServerVerification; - return false; - } + if (verify_result_ != X509_V_OK) { + error_ = Error::SSLServerVerification; + return false; + } - auto server_cert = SSL_get_peer_certificate(ssl); + auto server_cert = SSL_get_peer_certificate(ssl); - if (server_cert == nullptr) { - error_ = Error::SSLServerVerification; - return false; - } + if (server_cert == nullptr) { + error_ = Error::SSLServerVerification; + return false; + } - if (!verify_host(server_cert)) { - X509_free(server_cert); - error_ = Error::SSLServerVerification; - return false; - } - X509_free(server_cert); - } + if (!verify_host(server_cert)) { + X509_free(server_cert); + error_ = Error::SSLServerVerification; + return false; + } + X509_free(server_cert); + } - return true; - }, - [&](SSL *ssl) { - SSL_set_tlsext_host_name(ssl, host_.c_str()); - return true; - }); + return true; + }, + [&](SSL* ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + return true; + }); - if (ssl) { - socket.ssl = ssl; - return true; - } + if (ssl) { + socket.ssl = ssl; + return true; + } - close_socket(socket, false); - return false; + close_socket(socket, false); + return false; } -inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { - detail::close_socket(socket.sock); - socket_.sock = INVALID_SOCKET; - if (socket.ssl) { - detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); - socket_.ssl = nullptr; - } +inline void SSLClient::close_socket(Socket& socket, bool process_socket_ret) +{ + detail::close_socket(socket.sock); + socket_.sock = INVALID_SOCKET; + if (socket.ssl) { + detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); + socket_.ssl = nullptr; + } } inline bool -SSLClient::process_socket(Socket &socket, - std::function callback) { - assert(socket.ssl); - return detail::process_client_socket_ssl( - socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, - write_timeout_sec_, write_timeout_usec_, callback); +SSLClient::process_socket(Socket& socket, + std::function callback) +{ + assert(socket.ssl); + return detail::process_client_socket_ssl( + socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, callback); } inline bool SSLClient::is_ssl() const { return true; } -inline bool SSLClient::verify_host(X509 *server_cert) const { - /* Quote from RFC2818 section 3.1 "Server Identity" +inline bool SSLClient::verify_host(X509* server_cert) const +{ + /* Quote from RFC2818 section 3.1 "Server Identity" - If a subjectAltName extension of type dNSName is present, that MUST - be used as the identity. Otherwise, the (most specific) Common Name - field in the Subject field of the certificate MUST be used. Although - the use of the Common Name is existing practice, it is deprecated and - Certification Authorities are encouraged to use the dNSName instead. + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. - Matching is performed using the matching rules specified by - [RFC2459]. If more than one identity of a given type is present in - the certificate (e.g., more than one dNSName name, a match in any one - of the set is considered acceptable.) Names may contain the wildcard - character * which is considered to match any single domain name - component or component fragment. E.g., *.a.com matches foo.a.com but - not bar.foo.a.com. f*.com matches foo.com but not bar.com. + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. - In some cases, the URI is specified as an IP address rather than a - hostname. In this case, the iPAddress subjectAltName must be present - in the certificate and must exactly match the IP in the URI. + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. - */ - return verify_host_with_subject_alt_name(server_cert) || - verify_host_with_common_name(server_cert); + */ + return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert); } inline bool -SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { - auto ret = false; +SSLClient::verify_host_with_subject_alt_name(X509* server_cert) const +{ + auto ret = false; - auto type = GEN_DNS; + auto type = GEN_DNS; - struct in6_addr addr6; - struct in_addr addr; - size_t addr_len = 0; + struct in6_addr addr6; + struct in_addr addr; + size_t addr_len = 0; #ifndef __MINGW32__ - if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { - type = GEN_IPADD; - addr_len = sizeof(struct in6_addr); - } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { - type = GEN_IPADD; - addr_len = sizeof(struct in_addr); - } + if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { + type = GEN_IPADD; + addr_len = sizeof(struct in6_addr); + } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { + type = GEN_IPADD; + addr_len = sizeof(struct in_addr); + } #endif - auto alt_names = static_cast( - X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); - - if (alt_names) { - auto dsn_matched = false; - auto ip_mached = false; - - auto count = sk_GENERAL_NAME_num(alt_names); - - for (decltype(count) i = 0; i < count && !dsn_matched; i++) { - auto val = sk_GENERAL_NAME_value(alt_names, i); - if (val->type == type) { - auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); - auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); - - if (strlen(name) == name_len) { - switch (type) { - case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; - - case GEN_IPADD: - if (!memcmp(&addr6, name, addr_len) || - !memcmp(&addr, name, addr_len)) { - ip_mached = true; + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); + + if (alt_names) { + auto dsn_matched = false; + auto ip_mached = false; + + auto count = sk_GENERAL_NAME_num(alt_names); + + for (decltype(count) i = 0; i < count && !dsn_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == type) { + auto name = (const char*)ASN1_STRING_get0_data(val->d.ia5); + auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); + + if (strlen(name) == name_len) { + switch (type) { + case GEN_DNS: + dsn_matched = check_host_name(name, name_len); + break; + + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) { + ip_mached = true; + } + break; + } + } } - break; - } } - } - } - if (dsn_matched || ip_mached) { ret = true; } - } + if (dsn_matched || ip_mached) { + ret = true; + } + } - GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); - return ret; + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME)*)alt_names); + return ret; } -inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { - const auto subject_name = X509_get_subject_name(server_cert); +inline bool SSLClient::verify_host_with_common_name(X509* server_cert) const +{ + const auto subject_name = X509_get_subject_name(server_cert); - if (subject_name != nullptr) { - char name[BUFSIZ]; - auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, - name, sizeof(name)); + if (subject_name != nullptr) { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); - if (name_len != -1) { - return check_host_name(name, static_cast(name_len)); + if (name_len != -1) { + return check_host_name(name, static_cast(name_len)); + } } - } - return false; + return false; } -inline bool SSLClient::check_host_name(const char *pattern, - size_t pattern_len) const { - if (host_.size() == pattern_len && host_ == pattern) { return true; } +inline bool SSLClient::check_host_name(const char* pattern, + size_t pattern_len) const +{ + if (host_.size() == pattern_len && host_ == pattern) { + return true; + } - // Wildcard match - // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 - std::vector pattern_components; - detail::split(&pattern[0], &pattern[pattern_len], '.', - [&](const char *b, const char *e) { - pattern_components.emplace_back(std::string(b, e)); - }); + // Wildcard match + // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 + std::vector pattern_components; + detail::split(&pattern[0], &pattern[pattern_len], '.', + [&](const char* b, const char* e) { + pattern_components.emplace_back(std::string(b, e)); + }); - if (host_components_.size() != pattern_components.size()) { return false; } + if (host_components_.size() != pattern_components.size()) { + return false; + } - auto itr = pattern_components.begin(); - for (const auto &h : host_components_) { - auto &p = *itr; - if (p != h && p != "*") { - auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && - !p.compare(0, p.size() - 1, h)); - if (!partial_match) { return false; } + auto itr = pattern_components.begin(); + for (const auto& h : host_components_) { + auto& p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && !p.compare(0, p.size() - 1, h)); + if (!partial_match) { + return false; + } + } + ++itr; } - ++itr; - } - return true; + return true; } #endif // Universal client implementation -inline Client::Client(const char *scheme_host_port) - : Client(scheme_host_port, std::string(), std::string()) {} +inline Client::Client(const char* scheme_host_port) + : Client(scheme_host_port, std::string(), std::string()) +{ +} -inline Client::Client(const char *scheme_host_port, - const std::string &client_cert_path, - const std::string &client_key_path) { - const static std::regex re(R"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)"); +inline Client::Client(const char* scheme_host_port, + const std::string& client_cert_path, + const std::string& client_key_path) +{ + const static std::regex re(R"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)"); - std::cmatch m; - if (std::regex_match(scheme_host_port, m, re)) { - auto scheme = m[1].str(); + std::cmatch m; + if (std::regex_match(scheme_host_port, m, re)) { + auto scheme = m[1].str(); #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - if (!scheme.empty() && (scheme != "http" && scheme != "https")) { + if (!scheme.empty() && (scheme != "http" && scheme != "https")) { #else - if (!scheme.empty() && scheme != "http") { + if (!scheme.empty() && scheme != "http") { #endif - return; - } + return; + } - auto is_ssl = scheme == "https"; + auto is_ssl = scheme == "https"; - auto host = m[2].str(); + auto host = m[2].str(); - auto port_str = m[3].str(); - auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80); + auto port_str = m[3].str(); + auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80); - if (is_ssl) { + if (is_ssl) { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - cli_ = std::make_shared(host.c_str(), port, client_cert_path, - client_key_path); - is_ssl_ = is_ssl; + cli_ = std::make_shared(host.c_str(), port, client_cert_path, + client_key_path); + is_ssl_ = is_ssl; #endif + } else { + cli_ = std::make_shared(host.c_str(), port, client_cert_path, + client_key_path); + } } else { - cli_ = std::make_shared(host.c_str(), port, client_cert_path, - client_key_path); + cli_ = std::make_shared(scheme_host_port, 80, client_cert_path, + client_key_path); } - } else { - cli_ = std::make_shared(scheme_host_port, 80, client_cert_path, - client_key_path); - } -} - -inline Client::Client(const std::string &host, int port) - : cli_(std::make_shared(host, port)) {} - -inline Client::Client(const std::string &host, int port, - const std::string &client_cert_path, - const std::string &client_key_path) - : cli_(std::make_shared(host, port, client_cert_path, - client_key_path)) {} - -inline Client::~Client() {} - -inline bool Client::is_valid() const { - return cli_ != nullptr && cli_->is_valid(); -} - -inline Result Client::Get(const char *path) { return cli_->Get(path); } -inline Result Client::Get(const char *path, const Headers &headers) { - return cli_->Get(path, headers); -} -inline Result Client::Get(const char *path, Progress progress) { - return cli_->Get(path, progress); -} -inline Result Client::Get(const char *path, const Headers &headers, - Progress progress) { - return cli_->Get(path, headers, progress); -} -inline Result Client::Get(const char *path, ContentReceiver content_receiver) { - return cli_->Get(path, std::move(content_receiver)); -} -inline Result Client::Get(const char *path, const Headers &headers, - ContentReceiver content_receiver) { - return cli_->Get(path, headers, std::move(content_receiver)); -} -inline Result Client::Get(const char *path, ContentReceiver content_receiver, - Progress progress) { - return cli_->Get(path, std::move(content_receiver), std::move(progress)); -} -inline Result Client::Get(const char *path, const Headers &headers, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, headers, std::move(content_receiver), - std::move(progress)); -} -inline Result Client::Get(const char *path, ResponseHandler response_handler, - ContentReceiver content_receiver) { - return cli_->Get(path, std::move(response_handler), - std::move(content_receiver)); -} -inline Result Client::Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return cli_->Get(path, headers, std::move(response_handler), - std::move(content_receiver)); -} -inline Result Client::Get(const char *path, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, std::move(response_handler), - std::move(content_receiver), std::move(progress)); -} -inline Result Client::Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, headers, response_handler, content_receiver, progress); -} - -inline Result Client::Head(const char *path) { return cli_->Head(path); } -inline Result Client::Head(const char *path, const Headers &headers) { - return cli_->Head(path, headers); } -inline Result Client::Post(const char *path) { return cli_->Post(path); } -inline Result Client::Post(const char *path, const std::string &body, - const char *content_type) { - return cli_->Post(path, body, content_type); -} -inline Result Client::Post(const char *path, const Headers &headers, - const std::string &body, const char *content_type) { - return cli_->Post(path, headers, body, content_type); -} -inline Result Client::Post(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return cli_->Post(path, content_length, content_provider, content_type); -} -inline Result Client::Post(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return cli_->Post(path, headers, content_length, content_provider, - content_type); -} -inline Result Client::Post(const char *path, const Params ¶ms) { - return cli_->Post(path, params); -} -inline Result Client::Post(const char *path, const Headers &headers, - const Params ¶ms) { - return cli_->Post(path, headers, params); -} -inline Result Client::Post(const char *path, - const MultipartFormDataItems &items) { - return cli_->Post(path, items); -} -inline Result Client::Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items) { - return cli_->Post(path, headers, items); -} -inline Result Client::Put(const char *path) { return cli_->Put(path); } -inline Result Client::Put(const char *path, const std::string &body, - const char *content_type) { - return cli_->Put(path, body, content_type); -} -inline Result Client::Put(const char *path, const Headers &headers, - const std::string &body, const char *content_type) { - return cli_->Put(path, headers, body, content_type); -} -inline Result Client::Put(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return cli_->Put(path, content_length, content_provider, content_type); -} -inline Result Client::Put(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return cli_->Put(path, headers, content_length, content_provider, - content_type); -} -inline Result Client::Put(const char *path, const Params ¶ms) { - return cli_->Put(path, params); -} -inline Result Client::Put(const char *path, const Headers &headers, - const Params ¶ms) { - return cli_->Put(path, headers, params); -} -inline Result Client::Patch(const char *path, const std::string &body, - const char *content_type) { - return cli_->Patch(path, body, content_type); -} -inline Result Client::Patch(const char *path, const Headers &headers, - const std::string &body, const char *content_type) { - return cli_->Patch(path, headers, body, content_type); -} -inline Result Client::Patch(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return cli_->Patch(path, content_length, content_provider, content_type); -} -inline Result Client::Patch(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { - return cli_->Patch(path, headers, content_length, content_provider, - content_type); -} -inline Result Client::Delete(const char *path) { return cli_->Delete(path); } -inline Result Client::Delete(const char *path, const std::string &body, - const char *content_type) { - return cli_->Delete(path, body, content_type); -} -inline Result Client::Delete(const char *path, const Headers &headers) { - return cli_->Delete(path, headers); -} -inline Result Client::Delete(const char *path, const Headers &headers, - const std::string &body, - const char *content_type) { - return cli_->Delete(path, headers, body, content_type); -} -inline Result Client::Options(const char *path) { return cli_->Options(path); } -inline Result Client::Options(const char *path, const Headers &headers) { - return cli_->Options(path, headers); +inline Client::Client(const std::string& host, int port) + : cli_(std::make_shared(host, port)) +{ } -inline bool Client::send(const Request &req, Response &res) { - return cli_->send(req, res); +inline Client::Client(const std::string& host, int port, + const std::string& client_cert_path, + const std::string& client_key_path) + : cli_(std::make_shared(host, port, client_cert_path, + client_key_path)) +{ +} + +inline Client::~Client() { } + +inline bool Client::is_valid() const +{ + return cli_ != nullptr && cli_->is_valid(); +} + +inline Result Client::Get(const char* path) { return cli_->Get(path); } +inline Result Client::Get(const char* path, const Headers& headers) +{ + return cli_->Get(path, headers); +} +inline Result Client::Get(const char* path, Progress progress) +{ + return cli_->Get(path, progress); +} +inline Result Client::Get(const char* path, const Headers& headers, + Progress progress) +{ + return cli_->Get(path, headers, progress); +} +inline Result Client::Get(const char* path, ContentReceiver content_receiver) +{ + return cli_->Get(path, std::move(content_receiver)); +} +inline Result Client::Get(const char* path, const Headers& headers, + ContentReceiver content_receiver) +{ + return cli_->Get(path, headers, std::move(content_receiver)); +} +inline Result Client::Get(const char* path, ContentReceiver content_receiver, + Progress progress) +{ + return cli_->Get(path, std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const char* path, const Headers& headers, + ContentReceiver content_receiver, Progress progress) +{ + return cli_->Get(path, headers, std::move(content_receiver), + std::move(progress)); +} +inline Result Client::Get(const char* path, ResponseHandler response_handler, + ContentReceiver content_receiver) +{ + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver)); +} +inline Result Client::Get(const char* path, const Headers& headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) +{ + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver)); +} +inline Result Client::Get(const char* path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) +{ + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const char* path, const Headers& headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) +{ + return cli_->Get(path, headers, response_handler, content_receiver, progress); +} + +inline Result Client::Head(const char* path) { return cli_->Head(path); } +inline Result Client::Head(const char* path, const Headers& headers) +{ + return cli_->Head(path, headers); +} + +inline Result Client::Post(const char* path) { return cli_->Post(path); } +inline Result Client::Post(const char* path, const std::string& body, + const char* content_type) +{ + return cli_->Post(path, body, content_type); +} +inline Result Client::Post(const char* path, const Headers& headers, + const std::string& body, const char* content_type) +{ + return cli_->Post(path, headers, body, content_type); +} +inline Result Client::Post(const char* path, size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return cli_->Post(path, content_length, content_provider, content_type); +} +inline Result Client::Post(const char* path, const Headers& headers, + size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return cli_->Post(path, headers, content_length, content_provider, + content_type); +} +inline Result Client::Post(const char* path, const Params& params) +{ + return cli_->Post(path, params); +} +inline Result Client::Post(const char* path, const Headers& headers, + const Params& params) +{ + return cli_->Post(path, headers, params); +} +inline Result Client::Post(const char* path, + const MultipartFormDataItems& items) +{ + return cli_->Post(path, items); +} +inline Result Client::Post(const char* path, const Headers& headers, + const MultipartFormDataItems& items) +{ + return cli_->Post(path, headers, items); +} +inline Result Client::Put(const char* path) { return cli_->Put(path); } +inline Result Client::Put(const char* path, const std::string& body, + const char* content_type) +{ + return cli_->Put(path, body, content_type); +} +inline Result Client::Put(const char* path, const Headers& headers, + const std::string& body, const char* content_type) +{ + return cli_->Put(path, headers, body, content_type); +} +inline Result Client::Put(const char* path, size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return cli_->Put(path, content_length, content_provider, content_type); +} +inline Result Client::Put(const char* path, const Headers& headers, + size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return cli_->Put(path, headers, content_length, content_provider, + content_type); +} +inline Result Client::Put(const char* path, const Params& params) +{ + return cli_->Put(path, params); +} +inline Result Client::Put(const char* path, const Headers& headers, + const Params& params) +{ + return cli_->Put(path, headers, params); +} +inline Result Client::Patch(const char* path, const std::string& body, + const char* content_type) +{ + return cli_->Patch(path, body, content_type); +} +inline Result Client::Patch(const char* path, const Headers& headers, + const std::string& body, const char* content_type) +{ + return cli_->Patch(path, headers, body, content_type); +} +inline Result Client::Patch(const char* path, size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return cli_->Patch(path, content_length, content_provider, content_type); +} +inline Result Client::Patch(const char* path, const Headers& headers, + size_t content_length, + ContentProvider content_provider, + const char* content_type) +{ + return cli_->Patch(path, headers, content_length, content_provider, + content_type); +} +inline Result Client::Delete(const char* path) { return cli_->Delete(path); } +inline Result Client::Delete(const char* path, const std::string& body, + const char* content_type) +{ + return cli_->Delete(path, body, content_type); +} +inline Result Client::Delete(const char* path, const Headers& headers) +{ + return cli_->Delete(path, headers); +} +inline Result Client::Delete(const char* path, const Headers& headers, + const std::string& body, + const char* content_type) +{ + return cli_->Delete(path, headers, body, content_type); +} +inline Result Client::Options(const char* path) { return cli_->Options(path); } +inline Result Client::Options(const char* path, const Headers& headers) +{ + return cli_->Options(path, headers); +} + +inline bool Client::send(const Request& req, Response& res) +{ + return cli_->send(req, res); } inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); } inline void Client::stop() { cli_->stop(); } -inline void Client::set_default_headers(Headers headers) { - cli_->set_default_headers(std::move(headers)); +inline void Client::set_default_headers(Headers headers) +{ + cli_->set_default_headers(std::move(headers)); } inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } -inline void Client::set_socket_options(SocketOptions socket_options) { - cli_->set_socket_options(socket_options); +inline void Client::set_socket_options(SocketOptions socket_options) +{ + cli_->set_socket_options(socket_options); } -inline void Client::set_connection_timeout(time_t sec, time_t usec) { - cli_->set_connection_timeout(sec, usec); +inline void Client::set_connection_timeout(time_t sec, time_t usec) +{ + cli_->set_connection_timeout(sec, usec); } -inline void Client::set_read_timeout(time_t sec, time_t usec) { - cli_->set_read_timeout(sec, usec); +inline void Client::set_read_timeout(time_t sec, time_t usec) +{ + cli_->set_read_timeout(sec, usec); } -inline void Client::set_write_timeout(time_t sec, time_t usec) { - cli_->set_write_timeout(sec, usec); +inline void Client::set_write_timeout(time_t sec, time_t usec) +{ + cli_->set_write_timeout(sec, usec); } -inline void Client::set_basic_auth(const char *username, const char *password) { - cli_->set_basic_auth(username, password); +inline void Client::set_basic_auth(const char* username, const char* password) +{ + cli_->set_basic_auth(username, password); } -inline void Client::set_bearer_token_auth(const char *token) { - cli_->set_bearer_token_auth(token); +inline void Client::set_bearer_token_auth(const char* token) +{ + cli_->set_bearer_token_auth(token); } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void Client::set_digest_auth(const char *username, - const char *password) { - cli_->set_digest_auth(username, password); +inline void Client::set_digest_auth(const char* username, + const char* password) +{ + cli_->set_digest_auth(username, password); } #endif inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); } -inline void Client::set_follow_location(bool on) { - cli_->set_follow_location(on); +inline void Client::set_follow_location(bool on) +{ + cli_->set_follow_location(on); } inline void Client::set_compress(bool on) { cli_->set_compress(on); } inline void Client::set_decompress(bool on) { cli_->set_decompress(on); } -inline void Client::set_interface(const char *intf) { - cli_->set_interface(intf); +inline void Client::set_interface(const char* intf) +{ + cli_->set_interface(intf); } -inline void Client::set_proxy(const char *host, int port) { - cli_->set_proxy(host, port); +inline void Client::set_proxy(const char* host, int port) +{ + cli_->set_proxy(host, port); } -inline void Client::set_proxy_basic_auth(const char *username, - const char *password) { - cli_->set_proxy_basic_auth(username, password); +inline void Client::set_proxy_basic_auth(const char* username, + const char* password) +{ + cli_->set_proxy_basic_auth(username, password); } -inline void Client::set_proxy_bearer_token_auth(const char *token) { - cli_->set_proxy_bearer_token_auth(token); +inline void Client::set_proxy_bearer_token_auth(const char* token) +{ + cli_->set_proxy_bearer_token_auth(token); } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline void Client::set_proxy_digest_auth(const char *username, - const char *password) { - cli_->set_proxy_digest_auth(username, password); +inline void Client::set_proxy_digest_auth(const char* username, + const char* password) +{ + cli_->set_proxy_digest_auth(username, password); } #endif inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -inline Client &Client::set_ca_cert_path(const char *ca_cert_file_path, - const char *ca_cert_dir_path) { - if (is_ssl_) { - static_cast(*cli_).set_ca_cert_path(ca_cert_file_path, - ca_cert_dir_path); - } - return *this; +inline Client& Client::set_ca_cert_path(const char* ca_cert_file_path, + const char* ca_cert_dir_path) +{ + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_path(ca_cert_file_path, + ca_cert_dir_path); + } + return *this; } -inline Client &Client::set_ca_cert_store(X509_STORE *ca_cert_store) { - if (is_ssl_) { - static_cast(*cli_).set_ca_cert_store(ca_cert_store); - } - return *this; +inline Client& Client::set_ca_cert_store(X509_STORE* ca_cert_store) +{ + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_store(ca_cert_store); + } + return *this; } -inline Client &Client::enable_server_certificate_verification(bool enabled) { - if (is_ssl_) { - static_cast(*cli_).enable_server_certificate_verification( - enabled); - } - return *this; +inline Client& Client::enable_server_certificate_verification(bool enabled) +{ + if (is_ssl_) { + static_cast(*cli_).enable_server_certificate_verification( + enabled); + } + return *this; } -inline long Client::get_openssl_verify_result() const { - if (is_ssl_) { - return static_cast(*cli_).get_openssl_verify_result(); - } - return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? +inline long Client::get_openssl_verify_result() const +{ + if (is_ssl_) { + return static_cast(*cli_).get_openssl_verify_result(); + } + return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? } -inline SSL_CTX *Client::ssl_context() const { - if (is_ssl_) { return static_cast(*cli_).ssl_context(); } - return nullptr; +inline SSL_CTX* Client::ssl_context() const +{ + if (is_ssl_) { + return static_cast(*cli_).ssl_context(); + } + return nullptr; } #endif diff --git a/src/bb/image-io/rt.h b/src/bb/image-io/rt.h index 98f2fbb5..e59df26e 100644 --- a/src/bb/image-io/rt.h +++ b/src/bb/image-io/rt.h @@ -11,32 +11,32 @@ #include namespace ion { namespace bb { -namespace image_io { + namespace image_io { -std::map extern_functions; + std::map extern_functions; -class RegisterExtern { - public: - RegisterExtern(std::string key, Halide::ExternCFunction f) { - extern_functions[key] = f; - } -}; + class RegisterExtern { + public: + RegisterExtern(std::string key, Halide::ExternCFunction f) + { + extern_functions[key] = f; + } + }; -} // image_io + } // image_io } // bb } // ion #define ION_REGISTER_EXTERN(NAME) static auto ion_register_extern_##NAME = ion::bb::image_io::RegisterExtern(#NAME, NAME); -#include "rt_u3v.h" -#include "rt_file.h" #include "rt_display.h" +#include "rt_file.h" +#include "rt_u3v.h" #ifdef __linux__ #include "rt_realsense.h" #include "rt_v4l2.h" #endif - #undef ION_REGISTER_EXTERN #endif diff --git a/src/bb/image-io/rt_common.h b/src/bb/image-io/rt_common.h index c0475f89..d75e0aab 100644 --- a/src/bb/image-io/rt_common.h +++ b/src/bb/image-io/rt_common.h @@ -11,13 +11,12 @@ #include "opencv_loader.h" +#include "halide_image_io.h" #include #include -#include "halide_image_io.h" #include "ion/export.h" - #include "log.h" #include "httplib.h" @@ -35,294 +34,293 @@ namespace zip_file { namespace ion { namespace bb { -namespace image_io { - -template -std::string format(const char *fmt, const Rest &... rest) { - int length = snprintf(NULL, 0, fmt, rest...) + 1; // Explicit place for null termination - std::vector buf(length, 0); - snprintf(&buf[0], length, fmt, rest...); - std::string s(buf.begin(), std::find(buf.begin(), buf.end(), '\0')); - return s; -} - -// class DynamicModule { -// public: -// #ifdef _WIN32 -// using Handle = HMODULE; -// #else -// using Handle = void *; -// #endif -// -// DynamicModule(const std::string &module_name, bool essential) { -// ion::log::debug("Load module : Trying to load {}", module_name); -// if (module_name == "") { -// handle_ = nullptr; -// return; -// } -// -// #ifdef _WIN32 -// auto file_name = module_name + ".dll"; -// ion::log::debug("Load module : Looking for {}", file_name); -// handle_ = LoadLibraryA(file_name.c_str()); -// -// if (handle_ != nullptr){ -// //successfully loaded the module without the prefix of "lib". -// return; -// } -// -// file_name = "lib" + file_name; -// ion::log::debug("Load module : Looking for {}", file_name); -// handle_ = LoadLibraryA(file_name.c_str()); -// -// #else -// auto file_name = "lib" + module_name + ".so"; -// ion::log::debug("Load module : Looking for {}", file_name); -// handle_ = dlopen(file_name.c_str(), RTLD_NOW); -// #endif -// -// if (handle_ == nullptr) { -// if (essential) { -// throw std::runtime_error(get_error_string()); -// } else { -// std::cerr << format("WARNING: Not found the not essential dynamic library: %s, it may work as a simulation mode", module_name.c_str()); -// } -// } -// } -// -// ~DynamicModule() { -// if (handle_ != nullptr) { -// #ifdef _WIN32 -// FreeLibrary(handle_); -// #else -// dlclose(handle_); -// #endif -// } -// } -// -// DynamicModule(const std::string &module_name) -// : DynamicModule(module_name, true) { -// } -// -// bool is_available(void) const { -// return handle_ != NULL; -// } -// -// template -// T get_symbol(const std::string &symbol_name) const { -// #if defined(_WIN32) -// return reinterpret_cast(GetProcAddress(handle_, symbol_name.c_str())); -// #else -// return reinterpret_cast(dlsym(handle_, symbol_name.c_str())); -// #endif -// } -// -// private: -// std::string get_error_string(void) const { -// std::string error_msg; -// -// #ifdef _WIN32 -// LPVOID lpMsgBuf; -// FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | -// FORMAT_MESSAGE_IGNORE_INSERTS, -// nullptr, GetLastError(), -// MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), -// (LPTSTR)&lpMsgBuf, 0, nullptr); -// std::size_t len = 0; -// wcstombs_s(&len, nullptr, 0, reinterpret_cast(lpMsgBuf), _TRUNCATE); -// std::vector buf(len + 1, 0); -// wcstombs_s(nullptr, &buf[0], len, reinterpret_cast(lpMsgBuf), _TRUNCATE); -// error_msg.assign(buf.begin(), buf.end()); -// LocalFree(lpMsgBuf); -// #else -// const char *buf(dlerror()); -// error_msg.assign(buf ? buf : "none"); -// #endif -// return error_msg; -// } -// -// Handle handle_; -// }; - -std::tuple parse_url(const std::string &url) { - auto protocol_end_pos = url.find("://"); - if (protocol_end_pos == std::string::npos) - return std::tuple("", ""); - auto host_name_pos = protocol_end_pos + 3; - auto path_name_pos = url.find("/", host_name_pos); - auto host_name = url.substr(0, path_name_pos); - auto path_name = url.substr(path_name_pos); - return std::tuple(host_name, path_name); -} - -template -class ImageSequence { - - public: - ImageSequence(const std::string& session_id, const std::string& url) : idx_(0) { - namespace fs = std::filesystem; - - std::string host_name; - std::string path_name; - std::tie(host_name, path_name) = ion::bb::image_io::parse_url(url); - - std::vector data; - if (host_name.empty() || path_name.empty()) { - // fallback to local file - data.resize(fs::file_size(url)); - std::ifstream ifs(url, std::ios::binary); - ifs.read(reinterpret_cast(data.data()), data.size()); - } else { - httplib::Client cli(host_name.c_str()); - cli.set_follow_location(true); - auto res = cli.Get(path_name.c_str()); - if (res && res->status == 200) { - data.resize(res->body.size()); - std::memcpy(data.data(), res->body.c_str(), res->body.size()); - } else { - throw std::runtime_error("Failed to download"); - } + namespace image_io { + + template + std::string format(const char* fmt, const Rest&... rest) + { + int length = snprintf(NULL, 0, fmt, rest...) + 1; // Explicit place for null termination + std::vector buf(length, 0); + snprintf(&buf[0], length, fmt, rest...); + std::string s(buf.begin(), std::find(buf.begin(), buf.end(), '\0')); + return s; } - auto dir_path = fs::temp_directory_path() / session_id; - if (!fs::exists(dir_path)) { - if (!fs::create_directory(dir_path)) { - throw std::runtime_error("Failed to create temporary directory"); - } + // class DynamicModule { + // public: + // #ifdef _WIN32 + // using Handle = HMODULE; + // #else + // using Handle = void *; + // #endif + // + // DynamicModule(const std::string &module_name, bool essential) { + // ion::log::debug("Load module : Trying to load {}", module_name); + // if (module_name == "") { + // handle_ = nullptr; + // return; + // } + // + // #ifdef _WIN32 + // auto file_name = module_name + ".dll"; + // ion::log::debug("Load module : Looking for {}", file_name); + // handle_ = LoadLibraryA(file_name.c_str()); + // + // if (handle_ != nullptr){ + // //successfully loaded the module without the prefix of "lib". + // return; + // } + // + // file_name = "lib" + file_name; + // ion::log::debug("Load module : Looking for {}", file_name); + // handle_ = LoadLibraryA(file_name.c_str()); + // + // #else + // auto file_name = "lib" + module_name + ".so"; + // ion::log::debug("Load module : Looking for {}", file_name); + // handle_ = dlopen(file_name.c_str(), RTLD_NOW); + // #endif + // + // if (handle_ == nullptr) { + // if (essential) { + // throw std::runtime_error(get_error_string()); + // } else { + // std::cerr << format("WARNING: Not found the not essential dynamic library: %s, it may work as a simulation mode", module_name.c_str()); + // } + // } + // } + // + // ~DynamicModule() { + // if (handle_ != nullptr) { + // #ifdef _WIN32 + // FreeLibrary(handle_); + // #else + // dlclose(handle_); + // #endif + // } + // } + // + // DynamicModule(const std::string &module_name) + // : DynamicModule(module_name, true) { + // } + // + // bool is_available(void) const { + // return handle_ != NULL; + // } + // + // template + // T get_symbol(const std::string &symbol_name) const { + // #if defined(_WIN32) + // return reinterpret_cast(GetProcAddress(handle_, symbol_name.c_str())); + // #else + // return reinterpret_cast(dlsym(handle_, symbol_name.c_str())); + // #endif + // } + // + // private: + // std::string get_error_string(void) const { + // std::string error_msg; + // + // #ifdef _WIN32 + // LPVOID lpMsgBuf; + // FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + // FORMAT_MESSAGE_IGNORE_INSERTS, + // nullptr, GetLastError(), + // MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + // (LPTSTR)&lpMsgBuf, 0, nullptr); + // std::size_t len = 0; + // wcstombs_s(&len, nullptr, 0, reinterpret_cast(lpMsgBuf), _TRUNCATE); + // std::vector buf(len + 1, 0); + // wcstombs_s(nullptr, &buf[0], len, reinterpret_cast(lpMsgBuf), _TRUNCATE); + // error_msg.assign(buf.begin(), buf.end()); + // LocalFree(lpMsgBuf); + // #else + // const char *buf(dlerror()); + // error_msg.assign(buf ? buf : "none"); + // #endif + // return error_msg; + // } + // + // Handle handle_; + // }; + + std::tuple parse_url(const std::string& url) + { + auto protocol_end_pos = url.find("://"); + if (protocol_end_pos == std::string::npos) + return std::tuple("", ""); + auto host_name_pos = protocol_end_pos + 3; + auto path_name_pos = url.find("/", host_name_pos); + auto host_name = url.substr(0, path_name_pos); + auto path_name = url.substr(path_name_pos); + return std::tuple(host_name, path_name); } - if (fs::path(url).extension() == ".zip") { - zip_file::miniz_cpp::zip_file zf(data); - zf.extractall(dir_path.string()); - } else { - std::ofstream ofs(dir_path / fs::path(url).filename(), std::ios::binary); - ofs.write(reinterpret_cast(data.data()), data.size()); - } - - for (auto& d : fs::directory_iterator(dir_path)) { - paths_.push_back(d.path()); - } - // Dictionary order - std::sort(paths_.begin(), paths_.end()); + template + class ImageSequence { + + public: + ImageSequence(const std::string& session_id, const std::string& url) + : idx_(0) + { + namespace fs = std::filesystem; + + std::string host_name; + std::string path_name; + std::tie(host_name, path_name) = ion::bb::image_io::parse_url(url); + + std::vector data; + if (host_name.empty() || path_name.empty()) { + // fallback to local file + data.resize(fs::file_size(url)); + std::ifstream ifs(url, std::ios::binary); + ifs.read(reinterpret_cast(data.data()), data.size()); + } else { + httplib::Client cli(host_name.c_str()); + cli.set_follow_location(true); + auto res = cli.Get(path_name.c_str()); + if (res && res->status == 200) { + data.resize(res->body.size()); + std::memcpy(data.data(), res->body.c_str(), res->body.size()); + } else { + throw std::runtime_error("Failed to download"); + } + } - } + auto dir_path = fs::temp_directory_path() / session_id; + if (!fs::exists(dir_path)) { + if (!fs::create_directory(dir_path)) { + throw std::runtime_error("Failed to create temporary directory"); + } + } - void get(int width, int height, int imread_flags, Halide::Runtime::Buffer &buf) { - namespace fs = std::filesystem; + if (fs::path(url).extension() == ".zip") { + zip_file::miniz_cpp::zip_file zf(data); + zf.extractall(dir_path.string()); + } else { + std::ofstream ofs(dir_path / fs::path(url).filename(), std::ios::binary); + ofs.write(reinterpret_cast(data.data()), data.size()); + } - auto path = paths_[idx_]; - auto size = fs::file_size(path); + for (auto& d : fs::directory_iterator(dir_path)) { + paths_.push_back(d.path()); + } + // Dictionary order + std::sort(paths_.begin(), paths_.end()); + } - std::ifstream ifs(path, std::ios::binary); - std::vector img_data(size); - ifs.read(reinterpret_cast(img_data.data()), size); - if (path.extension() == ".raw") { - switch (imread_flags) { - case IMREAD_GRAYSCALE: - if (size == width * height * sizeof(uint8_t)) { - Halide::Runtime::Buffer buf_8(std::vector{width, height}); //read in 8 bit - std::memcpy(buf_8.data(), img_data.data(), size); // set_img_data - auto buf_16 = Halide::Tools::ImageTypeConversion::convert_image(buf_8, halide_type_of()); - buf.copy_from(buf_16); - } else if (size == width * height * sizeof(uint16_t)) { - std::memcpy(buf.data(), img_data.data(), size); - } else { - throw std::runtime_error("Unsupported raw format"); + void get(int width, int height, int imread_flags, Halide::Runtime::Buffer& buf) + { + namespace fs = std::filesystem; + + auto path = paths_[idx_]; + auto size = fs::file_size(path); + + std::ifstream ifs(path, std::ios::binary); + std::vector img_data(size); + ifs.read(reinterpret_cast(img_data.data()), size); + if (path.extension() == ".raw") { + switch (imread_flags) { + case IMREAD_GRAYSCALE: + if (size == width * height * sizeof(uint8_t)) { + Halide::Runtime::Buffer buf_8(std::vector { width, height }); // read in 8 bit + std::memcpy(buf_8.data(), img_data.data(), size); // set_img_data + auto buf_16 = Halide::Tools::ImageTypeConversion::convert_image(buf_8, halide_type_of()); + buf.copy_from(buf_16); + } else if (size == width * height * sizeof(uint16_t)) { + std::memcpy(buf.data(), img_data.data(), size); + } else { + throw std::runtime_error("Unsupported raw format"); + } + break; + case IMREAD_COLOR: + if (size == 3 * width * height * sizeof(uint8_t)) { + // Expect interleaved RGB + Halide::Runtime::Buffer buf_interleaved = Halide::Runtime::Buffer::make_interleaved(width, height, 3); + ; + std::memcpy(buf_interleaved.data(), img_data.data(), size); // set_img_data + auto buffer_planar = buf_interleaved.copy_to_planar(); + buf.copy_from(buffer_planar); + } else { + throw std::runtime_error("Unsupported raw format"); + } + break; + default: + throw std::runtime_error("Unsupported flags"); } - break; - case IMREAD_COLOR: - if (size == 3 * width * height * sizeof(uint8_t)) { - // Expect interleaved RGB - Halide::Runtime::Buffer buf_interleaved = Halide::Runtime::Buffer ::make_interleaved(width, height, 3); ; - std::memcpy(buf_interleaved.data(), img_data.data(), size); // set_img_data - auto buffer_planar = buf_interleaved.copy_to_planar(); - buf.copy_from(buffer_planar); - } else { - throw std::runtime_error("Unsupported raw format"); + } else { + switch (imread_flags) { + case IMREAD_GRAYSCALE: { + Halide::Runtime::Buffer img_buf = Halide::Tools::load_and_convert_image(path.string()); + buf.copy_from(img_buf); + } break; + case IMREAD_COLOR: { + Halide::Runtime::Buffer img_buf = Halide::Tools::load_image(path.string()); + buf.copy_from(img_buf); + } break; + default: + throw std::runtime_error("Unsupported flags"); } - break; - default: - throw std::runtime_error("Unsupported flags"); - } - } else { - switch (imread_flags) { - case IMREAD_GRAYSCALE: - { - Halide::Runtime::Buffer img_buf = Halide::Tools::load_and_convert_image(path.string()); - buf.copy_from(img_buf); } - break; - case IMREAD_COLOR: - { - Halide::Runtime::Buffer img_buf = Halide::Tools::load_image(path.string()); - buf.copy_from(img_buf); - } - break; - default: - throw std::runtime_error("Unsupported flags"); + idx_ = ((idx_ + 1) % paths_.size()); + return; } - } - idx_ = ((idx_+1) % paths_.size()); - return; - } - - private: - int32_t idx_; - std::vector paths_; -}; - - -struct rawHeader { - - // ---------- 0 - int version_; - // ---------- 4 - int width_; - int height_; - // ---------- 12 - float r_gain0_; - float g_gain0_; - float b_gain0_; - // ---------- 24 - float r_gain1_; - float g_gain1_; - float b_gain1_; - // ---------- 36 - int offset0_x_; - int offset0_y_; - int offset1_x_; - int offset1_y_; - // ---------- 52 - int outputsize0_x_; - int outputsize0_y_; - int outputsize1_x_; - int outputsize1_y_; - // ---------- 68 - float fps_; - // ---------- 72 - int pfnc_pixelformat; - int group_id; -}; + private: + int32_t idx_; + std::vector paths_; + }; + + struct rawHeader { + + // ---------- 0 + int version_; + // ---------- 4 + int width_; + int height_; + // ---------- 12 + float r_gain0_; + float g_gain0_; + float b_gain0_; + // ---------- 24 + float r_gain1_; + float g_gain1_; + float b_gain1_; + // ---------- 36 + int offset0_x_; + int offset0_y_; + int offset1_x_; + int offset1_y_; + // ---------- 52 + int outputsize0_x_; + int outputsize0_y_; + int outputsize1_x_; + int outputsize1_y_; + // ---------- 68 + float fps_; + // ---------- 72 + int pfnc_pixelformat; + int group_id; + }; // PFNC // https://www.emva.org/wp-content/uploads/GenICamPixelFormatValues.pdf -#define PFNC_Mono8 0x01080001 //PFNC Monochrome 8-bit -#define PFNC_Mono10 0x01100003 //PFNC Monochrome 10-bit unpacked -#define PFNC_Mono12 0x01100005 //PFNC Monochrome 12-bit unpacked -#define PFNC_RGB8 0x02180014 //PFNC Red-Green-Blue 8-bit -#define PFNC_BGR8 0x02180015 //PFNC Blue-Green-Red 8-bit - -#define PFNC_BayerBG8 0x0108000B //PFNC Bayer Blue-Green 8-bit -#define PFNC_BayerBG10 0x0110000F //PFNC Bayer Blue-Green 10-bit unpacked -#define PFNC_BayerBG12 0x01100013 //PFNC Bayer Blue-Green 12-bit unpacked - -#define PFNC_BayerGR8 0x01080008 //PFNC Bayer Green-Red 8-bit -#define PFNC_BayerGR12 0x01100010 //PFNC Bayer Green-Red 12-bit unpacked -#define PFNC_YCbCr422_8 0x0210003B //PFNC YCbCr 4:2:2 8-bit - -} // namespace image_io -} // namespace bb -} // namespace ion +#define PFNC_Mono8 0x01080001 // PFNC Monochrome 8-bit +#define PFNC_Mono10 0x01100003 // PFNC Monochrome 10-bit unpacked +#define PFNC_Mono12 0x01100005 // PFNC Monochrome 12-bit unpacked +#define PFNC_RGB8 0x02180014 // PFNC Red-Green-Blue 8-bit +#define PFNC_BGR8 0x02180015 // PFNC Blue-Green-Red 8-bit + +#define PFNC_BayerBG8 0x0108000B // PFNC Bayer Blue-Green 8-bit +#define PFNC_BayerBG10 0x0110000F // PFNC Bayer Blue-Green 10-bit unpacked +#define PFNC_BayerBG12 0x01100013 // PFNC Bayer Blue-Green 12-bit unpacked + +#define PFNC_BayerGR8 0x01080008 // PFNC Bayer Green-Red 8-bit +#define PFNC_BayerGR12 0x01100010 // PFNC Bayer Green-Red 12-bit unpacked +#define PFNC_YCbCr422_8 0x0210003B // PFNC YCbCr 4:2:2 8-bit + + } // namespace image_io +} // namespace bb +} // namespace ion #endif diff --git a/src/bb/image-io/rt_display.h b/src/bb/image-io/rt_display.h index 5a17039e..170ad0b7 100644 --- a/src/bb/image-io/rt_display.h +++ b/src/bb/image-io/rt_display.h @@ -22,11 +22,14 @@ class FBDev { public: FBDev(int32_t width, int32_t height) - : width_(width), height_(height), device_is_available_(true) { + : width_(width) + , height_(height) + , device_is_available_(true) + { using namespace std; - const char *dev_name = "/dev/fb0"; + const char* dev_name = "/dev/fb0"; fd_ = open(dev_name, O_RDWR); if (fd_ == -1) { @@ -49,24 +52,26 @@ class FBDev { unsigned int screensize = (vinfo_.xres * vinfo_.yres * vinfo_.bits_per_pixel) >> 3; /* (>>3): bits to bytes */ - fbp_ = reinterpret_cast(mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); - if (fbp_ == reinterpret_cast(-1)) { + fbp_ = reinterpret_cast(mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); + if (fbp_ == reinterpret_cast(-1)) { throw runtime_error("Error mapping framebuffer device to memory"); } } - void put(Halide::Runtime::Buffer &buf) { + void put(Halide::Runtime::Buffer& buf) + { for (int y = 0; y < height_; y++) { for (int x = 0; x < width_; x++) { unsigned int location = (x + vinfo_.xoffset) * (vinfo_.bits_per_pixel >> 3) + (y + vinfo_.yoffset) * finfo_.line_length; - fbp_[location + 0] = buf(2, x, y); // B - fbp_[location + 1] = buf(1, x, y); // G - fbp_[location + 2] = buf(0, x, y); // R + fbp_[location + 0] = buf(2, x, y); // B + fbp_[location + 1] = buf(1, x, y); // G + fbp_[location + 2] = buf(0, x, y); // R } } } - bool is_available() { + bool is_available() + { return device_is_available_; } @@ -74,15 +79,16 @@ class FBDev { int32_t width_; int32_t height_; int fd_; - uint8_t *fbp_; + uint8_t* fbp_; struct fb_fix_screeninfo finfo_; struct fb_var_screeninfo vinfo_; bool device_is_available_; }; -} // namespace +} // namespace -extern "C" int ION_EXPORT ion_bb_image_io_fb_display(int32_t width, int32_t height, halide_buffer_t *in, halide_buffer_t *out) { +extern "C" int ION_EXPORT ion_bb_image_io_fb_display(int32_t width, int32_t height, halide_buffer_t* in, halide_buffer_t* out) +{ try { static FBDev fbdev(width, height); if (in->is_bounds_query() || out->is_bounds_query()) { @@ -101,7 +107,7 @@ extern "C" int ION_EXPORT ion_bb_image_io_fb_display(int32_t width, int32_t heig } } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return -1; } catch (...) { @@ -111,10 +117,11 @@ extern "C" int ION_EXPORT ion_bb_image_io_fb_display(int32_t width, int32_t heig } #endif -extern "C" ION_EXPORT int ion_bb_image_io_gui_display(halide_buffer_t *in, int width, int height, int idx, halide_buffer_t *out) { +extern "C" ION_EXPORT int ion_bb_image_io_gui_display(halide_buffer_t* in, int width, int height, int idx, halide_buffer_t* out) +{ if (in->is_bounds_query()) { in->dim[0].min = 0; - in->dim[0].extent = 3; // RGB + in->dim[0].extent = 3; // RGB in->dim[1].min = 0; in->dim[1].extent = width; in->dim[2].min = 0; @@ -126,7 +133,7 @@ extern "C" ION_EXPORT int ion_bb_image_io_gui_display(halide_buffer_t *in, int w ibuf.copy_to_host(); auto img = cv.cvCreateMatHeader(height, width, CV_MAKETYPE(CV_8U, 3)); - cv.cvSetData(img, in->host, 3*width*sizeof(uint8_t)); + cv.cvSetData(img, in->host, 3 * width * sizeof(uint8_t)); auto name = "img" + std::to_string(idx); cv.cvShowImage(name.c_str(), img); diff --git a/src/bb/image-io/rt_file.h b/src/bb/image-io/rt_file.h index 5e075aee..2d89bc42 100644 --- a/src/bb/image-io/rt_file.h +++ b/src/bb/image-io/rt_file.h @@ -14,13 +14,13 @@ #include "log.h" -#include "rt_common.h" #include "httplib.h" +#include "rt_common.h" #include "opencv_loader.h" - -extern "C" int ION_EXPORT ion_bb_image_io_color_data_loader(halide_buffer_t *session_id_buf, halide_buffer_t *url_buf, int32_t width, int32_t height, halide_buffer_t *out) { +extern "C" int ION_EXPORT ion_bb_image_io_color_data_loader(halide_buffer_t* session_id_buf, halide_buffer_t* url_buf, int32_t width, int32_t height, halide_buffer_t* out) +{ using namespace ion::bb::image_io; @@ -29,8 +29,8 @@ extern "C" int ION_EXPORT ion_bb_image_io_color_data_loader(halide_buffer_t *ses return 0; } - const std::string session_id(reinterpret_cast(session_id_buf->host)); - const std::string url = reinterpret_cast(url_buf->host); + const std::string session_id(reinterpret_cast(session_id_buf->host)); + const std::string url = reinterpret_cast(url_buf->host); static std::unordered_map>> seqs; if (seqs.count(session_id) == 0) { seqs[session_id] = std::unique_ptr>(new ImageSequence(session_id, url)); @@ -38,7 +38,7 @@ extern "C" int ION_EXPORT ion_bb_image_io_color_data_loader(halide_buffer_t *ses Halide::Runtime::Buffer obuf(*out); seqs[session_id]->get(width, height, IMREAD_COLOR, obuf); - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return -1; } catch (...) { @@ -50,7 +50,8 @@ extern "C" int ION_EXPORT ion_bb_image_io_color_data_loader(halide_buffer_t *ses } ION_REGISTER_EXTERN(ion_bb_image_io_color_data_loader); -extern "C" int ION_EXPORT ion_bb_image_io_grayscale_data_loader(halide_buffer_t *session_id_buf, halide_buffer_t *url_buf, int32_t width, int32_t height, int32_t dynamic_range, halide_buffer_t *out) { +extern "C" int ION_EXPORT ion_bb_image_io_grayscale_data_loader(halide_buffer_t* session_id_buf, halide_buffer_t* url_buf, int32_t width, int32_t height, int32_t dynamic_range, halide_buffer_t* out) +{ using namespace ion::bb::image_io; @@ -61,8 +62,8 @@ extern "C" int ION_EXPORT ion_bb_image_io_grayscale_data_loader(halide_buffer_t out->dim[1].min = 0; out->dim[1].extent = height; } else { - const std::string session_id(reinterpret_cast(session_id_buf->host)); - const std::string url = reinterpret_cast(url_buf->host); + const std::string session_id(reinterpret_cast(session_id_buf->host)); + const std::string url = reinterpret_cast(url_buf->host); static std::unordered_map>> seqs; if (seqs.count(session_id) == 0) { seqs[session_id] = std::unique_ptr>(new ImageSequence(session_id, url)); @@ -70,7 +71,7 @@ extern "C" int ION_EXPORT ion_bb_image_io_grayscale_data_loader(halide_buffer_t Halide::Runtime::Buffer obuf(*out); seqs[session_id]->get(width, height, IMREAD_GRAYSCALE, obuf); } - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return -1; } catch (...) { @@ -82,7 +83,8 @@ extern "C" int ION_EXPORT ion_bb_image_io_grayscale_data_loader(halide_buffer_t } ION_REGISTER_EXTERN(ion_bb_image_io_grayscale_data_loader); -extern "C" int ION_EXPORT ion_bb_image_io_image_saver(halide_buffer_t *in, int32_t width, int32_t height, halide_buffer_t *path, halide_buffer_t *out) { +extern "C" int ION_EXPORT ion_bb_image_io_image_saver(halide_buffer_t* in, int32_t width, int32_t height, halide_buffer_t* path, halide_buffer_t* out) +{ try { if (in->is_bounds_query()) { in->dim[0].min = 0; @@ -93,10 +95,10 @@ extern "C" int ION_EXPORT ion_bb_image_io_image_saver(halide_buffer_t *in, int32 in->dim[2].extent = height; } else { Halide::Runtime::Buffer obuf = Halide::Runtime::Buffer::make_interleaved(width, height, 3); - std::memcpy(obuf.data(), in->host, 3* width*height*sizeof(uint8_t)); - Halide::Tools::save_image(obuf, reinterpret_cast(path->host)); + std::memcpy(obuf.data(), in->host, 3 * width * height * sizeof(uint8_t)); + Halide::Tools::save_image(obuf, reinterpret_cast(path->host)); } - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return -1; } catch (...) { @@ -108,7 +110,6 @@ extern "C" int ION_EXPORT ion_bb_image_io_image_saver(halide_buffer_t *in, int32 } ION_REGISTER_EXTERN(ion_bb_image_io_image_saver); - namespace { class Writer { @@ -117,23 +118,23 @@ class Writer { { if (instances.count(id) == 0) { - instances[id] = std::unique_ptr(new Writer(payload_size, output_directory, write_framecount, prefix )); + instances[id] = std::unique_ptr(new Writer(payload_size, output_directory, write_framecount, prefix)); } return *instances[id]; } - ~Writer() { - if (!disposed_){ + ~Writer() + { + if (!disposed_) { ion::log::debug("Trying to call dispose from distructor since disposed_ is {}", disposed_); dispose(); } - } - void post_image(std::vector& outs, std::vector& size, - ion::bb::image_io::rawHeader& header_info, void* framecounts) + void post_image(std::vector& outs, std::vector& size, + ion::bb::image_io::rawHeader& header_info, void* framecounts) { - if (with_header_){ + if (with_header_) { write_config_file(header_info); } ::std::unique_lock<::std::mutex> lock(mutex_); @@ -144,7 +145,7 @@ class Writer { uint8_t* buffer = buf_queue_.front(); buf_queue_.pop(); size_t offset = 0; - for (int i = 0; i < outs.size(); ++i){ + for (int i = 0; i < outs.size(); ++i) { ::std::memcpy(buffer + offset, reinterpret_cast(framecounts) + i, sizeof(int32_t)); offset += sizeof(int32_t); ::std::memcpy(buffer + offset, outs[i], size[i]); @@ -154,9 +155,9 @@ class Writer { task_cv_.notify_one(); } - void post_gendc(std::vector& outs, std::vector& size, ion::bb::image_io::rawHeader& header_info) + void post_gendc(std::vector& outs, std::vector& size, ion::bb::image_io::rawHeader& header_info) { - if (with_header_){ + if (with_header_) { write_config_file(header_info); } ::std::unique_lock<::std::mutex> lock(mutex_); @@ -167,7 +168,7 @@ class Writer { uint8_t* buffer = buf_queue_.front(); buf_queue_.pop(); size_t offset = 0; - for (int i = 0; i < outs.size(); ++i){ + for (int i = 0; i < outs.size(); ++i) { ::std::memcpy(buffer + offset, outs[i], size[i]); offset += size[i]; } @@ -175,8 +176,9 @@ class Writer { task_cv_.notify_one(); } - void dispose() { - ion::log::debug("Writer::dispose() :: is called"); + void dispose() + { + ion::log::debug("Writer::dispose() :: is called"); // Already disposed if thread is not joinable if (thread_ && thread_->joinable()) { keep_running_ = false; @@ -184,25 +186,25 @@ class Writer { thread_->join(); thread_ = nullptr; } - ion::log::debug("Writer::dispose() :: is finished"); - disposed_ = true; + ion::log::debug("Writer::dispose() :: is finished"); + disposed_ = true; } - - static void release_instance(const char * id) { + static void release_instance(const char* id) + { ion::log::debug("Writer::release_instance() :: is called"); if (instances.count(id) == 0) { - return; + return; } - Writer & writer = *instances[id].get(); + Writer& writer = *instances[id].get(); writer.dispose(); instances.erase(id); ion::log::debug("Writer::release_instance() :: Instance is delete"); + } - } - - void write_config_file(ion::bb::image_io::rawHeader& header_info){ + void write_config_file(ion::bb::image_io::rawHeader& header_info) + { nlohmann::json j_sensor; j_sensor["prefix"] = prefix_; j_sensor["framerate"] = header_info.fps_; @@ -220,12 +222,16 @@ class Writer { private: Writer(std::vector& payload_size, const std::string& output_directory, bool write_framecount, const std::string& prefix) - : keep_running_(true), output_directory_(output_directory), with_header_(true), disposed_(false), prefix_(prefix) + : keep_running_(true) + , output_directory_(output_directory) + , with_header_(true) + , disposed_(false) + , prefix_(prefix) { int total_payload_size = 0; - for (auto s : payload_size){ + for (auto s : payload_size) { total_payload_size += s; - if (write_framecount){ + if (write_framecount) { total_payload_size += sizeof(int32_t); } } @@ -240,29 +246,32 @@ class Writer { ofs_ = ::std::ofstream(output_directory_ / filename, ::std::ios::binary); } - int get_buffer_num(int width, int height, int num_sensor = 2, int data_in_byte = 2) { + int get_buffer_num(int width, int height, int num_sensor = 2, int data_in_byte = 2) + { // fix the memory size 2GB const double memory_size_in_MB = 2048.0; - return static_cast( memory_size_in_MB * 1024 * 1024 / width / height / num_sensor / data_in_byte); + return static_cast(memory_size_in_MB * 1024 * 1024 / width / height / num_sensor / data_in_byte); } - int get_buffer_num(int32_t payloadsize) { + int get_buffer_num(int32_t payloadsize) + { // fix the memory size 2GB const double memory_size_in_MB = 2048.0; - return static_cast( memory_size_in_MB * 1024 * 1024 / payloadsize); + return static_cast(memory_size_in_MB * 1024 * 1024 / payloadsize); } - static void entry_point(Writer* obj) { + static void entry_point(Writer* obj) + { try { obj->thread_main(); - } - catch (...) { + } catch (...) { ::std::unique_lock<::std::mutex> lock(obj->mutex_); obj->ep_ = ::std::current_exception(); } } - void thread_main() { + void thread_main() + { uint32_t frame_count; uint8_t* buffer; size_t size; @@ -315,7 +324,6 @@ class Writer { ofs_ = ::std::ofstream(output_directory_ / (prefix_ + ::std::to_string(file_idx++) + ".bin"), ::std::ios::binary); } - ofs_.write(reinterpret_cast(buffer), size); { @@ -330,7 +338,7 @@ class Writer { ofs_.close(); } - static ::std::unordered_map < ::std::string, std::unique_ptr> instances; // declares Writer::instance + static ::std::unordered_map<::std::string, std::unique_ptr> instances; // declares Writer::instance ::std::shared_ptr<::std::thread> thread_; ::std::vector<::std::vector> buffers_; ::std::mutex mutex_; @@ -350,28 +358,25 @@ class Writer { bool with_header_; }; -::std::unordered_map< ::std::string, std::unique_ptr> Writer::instances; // defines Writer::instance +::std::unordered_map<::std::string, std::unique_ptr> Writer::instances; // defines Writer::instance } // namespace - -extern "C" -int ION_EXPORT writer_dispose(const char *id) { +extern "C" int ION_EXPORT writer_dispose(const char* id) +{ Writer::release_instance(id); return 0; } - -extern "C" ION_EXPORT -int ion_bb_image_io_binary_gendc_saver( halide_buffer_t * id_buf, halide_buffer_t * gendc, halide_buffer_t * deviceinfo, - int payloadsize, halide_buffer_t* output_directory_buf, halide_buffer_t* prefix_buf, - halide_buffer_t * out) - { +extern "C" ION_EXPORT int ion_bb_image_io_binary_gendc_saver(halide_buffer_t* id_buf, halide_buffer_t* gendc, halide_buffer_t* deviceinfo, + int payloadsize, halide_buffer_t* output_directory_buf, halide_buffer_t* prefix_buf, + halide_buffer_t* out) +{ try { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); const ::std::string output_directory(reinterpret_cast(output_directory_buf->host)); - std::vectorpayloadsize_list{payloadsize}; + std::vector payloadsize_list { payloadsize }; const ::std::string prefix(reinterpret_cast(prefix_buf->host)); - auto& w(Writer::get_instance(id,payloadsize_list, output_directory, false, prefix)); + auto& w(Writer::get_instance(id, payloadsize_list, output_directory, false, prefix)); if (gendc->is_bounds_query() || deviceinfo->is_bounds_query()) { if (gendc->is_bounds_query()) { gendc->dim[0].min = 0; @@ -382,25 +387,20 @@ int ion_bb_image_io_binary_gendc_saver( halide_buffer_t * id_buf, halide_buffer_ deviceinfo->dim[0].extent = sizeof(ion::bb::image_io::rawHeader); } return 0; - } - else { + } else { ion::bb::image_io::rawHeader header_info; ::memcpy(&header_info, deviceinfo->host, sizeof(ion::bb::image_io::rawHeader)); - std::vector obufs{gendc->host}; - std::vector size_in_bytes{gendc->size_in_bytes()}; + std::vector obufs { gendc->host }; + std::vector size_in_bytes { gendc->size_in_bytes() }; w.post_gendc(obufs, size_in_bytes, header_info); - - } return 0; - } - catch (const ::std::exception& e) { + } catch (const ::std::exception& e) { ::std::cerr << e.what() << ::std::endl; return -1; - } - catch (...) { + } catch (...) { ::std::cerr << "Unknown error" << ::std::endl; return -1; } @@ -408,19 +408,18 @@ int ion_bb_image_io_binary_gendc_saver( halide_buffer_t * id_buf, halide_buffer_ ION_REGISTER_EXTERN(ion_bb_image_io_binary_gendc_saver); -extern "C" ION_EXPORT -int ion_bb_image_io_binary_image_saver( - halide_buffer_t * id_buf, - halide_buffer_t * image, halide_buffer_t * deviceinfo, halide_buffer_t * frame_count, - int width, int height, int dim, int byte_depth, halide_buffer_t* output_directory_buf, - halide_buffer_t* prefix_buf, - halide_buffer_t * out) - { +extern "C" ION_EXPORT int ion_bb_image_io_binary_image_saver( + halide_buffer_t* id_buf, + halide_buffer_t* image, halide_buffer_t* deviceinfo, halide_buffer_t* frame_count, + int width, int height, int dim, int byte_depth, halide_buffer_t* output_directory_buf, + halide_buffer_t* prefix_buf, + halide_buffer_t* out) +{ try { int num_output = 1; - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); int32_t frame_size = dim == 2 ? width * height * byte_depth : width * height * 3 * byte_depth; - std::vectorframe_size_list{frame_size}; + std::vector frame_size_list { frame_size }; const ::std::string output_directory(reinterpret_cast(output_directory_buf->host)); const ::std::string prefix(reinterpret_cast(prefix_buf->host)); auto& w(Writer::get_instance(id, frame_size_list, output_directory, true, prefix)); @@ -431,7 +430,7 @@ int ion_bb_image_io_binary_image_saver( image->dim[0].extent = width; image->dim[1].min = 0; image->dim[1].extent = height; - if (dim == 3){ + if (dim == 3) { image->dim[2].min = 0; image->dim[2].extent = 3; } @@ -445,24 +444,21 @@ int ion_bb_image_io_binary_image_saver( frame_count->dim[0].extent = num_output; } return 0; - } - else { + } else { ion::bb::image_io::rawHeader header_info; memcpy(&header_info, deviceinfo->host, sizeof(ion::bb::image_io::rawHeader)); - std::vector header_infos{header_info}; + std::vector header_infos { header_info }; - std::vector obufs{image->host}; - std::vector size_in_bytes{image->size_in_bytes()}; + std::vector obufs { image->host }; + std::vector size_in_bytes { image->size_in_bytes() }; w.post_image(obufs, size_in_bytes, header_info, frame_count->host); } return 0; - } - catch (const ::std::exception& e) { + } catch (const ::std::exception& e) { ::std::cerr << e.what() << ::std::endl; return -1; - } - catch (...) { + } catch (...) { ::std::cerr << "Unknown error" << ::std::endl; return -1; } @@ -472,154 +468,165 @@ ION_REGISTER_EXTERN(ion_bb_image_io_binary_image_saver); namespace { - class Reader { - public: - static Reader& get_instance(::std::string session_id, int width, int height, const ::std::string& output_directory) { - auto it = instances.find(session_id); - if (it == instances.end()) { - instances[session_id] = std::unique_ptr(new Reader(width, height, output_directory)); - } - - return *instances[session_id]; +class Reader { +public: + static Reader& get_instance(::std::string session_id, int width, int height, const ::std::string& output_directory) + { + auto it = instances.find(session_id); + if (it == instances.end()) { + instances[session_id] = std::unique_ptr(new Reader(width, height, output_directory)); } - void get(uint8_t* ptr0, uint8_t* ptr1, size_t size) { + return *instances[session_id]; + } - current_idx_ = file_idx_; + void get(uint8_t* ptr0, uint8_t* ptr1, size_t size) + { - if (finished_) { - return; - } + current_idx_ = file_idx_; - if (read_count_ < offset_frame_count_) { - ::std::memset(ptr0, 0, size); - ::std::memset(ptr1, 0, size); - } - else { - uint32_t frame_count = 0; - ifs_.read(reinterpret_cast(&frame_count), sizeof(frame_count)); + if (finished_) { + return; + } - if (frame_count != (latest_frame_count_ + 1)) { - ifs_.seekg(-static_cast(sizeof(frame_count)), ::std::ios::cur); - } - else { - ifs_.read(reinterpret_cast(latest_frame0_.data()), size); - ifs_.read(reinterpret_cast(latest_frame1_.data()), size); - } + if (read_count_ < offset_frame_count_) { + ::std::memset(ptr0, 0, size); + ::std::memset(ptr1, 0, size); + } else { + uint32_t frame_count = 0; + ifs_.read(reinterpret_cast(&frame_count), sizeof(frame_count)); + + if (frame_count != (latest_frame_count_ + 1)) { + ifs_.seekg(-static_cast(sizeof(frame_count)), ::std::ios::cur); + } else { + ifs_.read(reinterpret_cast(latest_frame0_.data()), size); + ifs_.read(reinterpret_cast(latest_frame1_.data()), size); + } - ::std::memcpy(ptr0, latest_frame0_.data(), size); - ::std::memcpy(ptr1, latest_frame1_.data(), size); + ::std::memcpy(ptr0, latest_frame0_.data(), size); + ::std::memcpy(ptr1, latest_frame1_.data(), size); - latest_frame_count_++; + latest_frame_count_++; - // rotate - ifs_.peek(); - if (ifs_.eof()) { - open_and_check(width_, height_, output_directory_, file_idx_, ifs_, &finished_); - if (finished_) { - ifs_ = ::std::ifstream(); - } + // rotate + ifs_.peek(); + if (ifs_.eof()) { + open_and_check(width_, height_, output_directory_, file_idx_, ifs_, &finished_); + if (finished_) { + ifs_ = ::std::ifstream(); } } - read_count_++; - } - - void close() { - ifs_.close(); } + read_count_++; + } - bool get_finished() const { - return finished_; - } + void close() + { + ifs_.close(); + } - uint32_t get_index() { - return current_idx_; - } + bool get_finished() const + { + return finished_; + } - void release_instance(const ::std::string& session_id) { - instances.erase(session_id); - } + uint32_t get_index() + { + return current_idx_; + } - private: - Reader(int width, int height, const ::std::string& output_directory) - : width_(width), height_(height), output_directory_(output_directory), - file_idx_(0), latest_frame0_(width* height), latest_frame1_(width* height), - latest_frame_count_((::std::numeric_limits::max)()), read_count_(0), finished_(false) - { + void release_instance(const ::std::string& session_id) + { + instances.erase(session_id); + } - open_and_check(width_, height_, output_directory_, file_idx_, ifs_, &finished_); - if (finished_) { - return; - } +private: + Reader(int width, int height, const ::std::string& output_directory) + : width_(width) + , height_(height) + , output_directory_(output_directory) + , file_idx_(0) + , latest_frame0_(width * height) + , latest_frame1_(width * height) + , latest_frame_count_((::std::numeric_limits::max)()) + , read_count_(0) + , finished_(false) + { - // Determine counter might be reset to zero (may dropped first few frames) - uint32_t prev_frame_count = 0; - const size_t size = static_cast(width * height * sizeof(uint16_t)); - while (true) { - uint32_t frame_count = 0; - ifs_.read(reinterpret_cast(&frame_count), sizeof(frame_count)); - ifs_.seekg(2 * size, ::std::ios::cur); - if (prev_frame_count > frame_count) { - ifs_.seekg(-static_cast(sizeof(frame_count)) - 2 * size, ::std::ios::cur); - offset_frame_count_ = frame_count; - break; - } - prev_frame_count = frame_count; - ifs_.peek(); + open_and_check(width_, height_, output_directory_, file_idx_, ifs_, &finished_); + if (finished_) { + return; + } - if (ifs_.eof()) { + // Determine counter might be reset to zero (may dropped first few frames) + uint32_t prev_frame_count = 0; + const size_t size = static_cast(width * height * sizeof(uint16_t)); + while (true) { + uint32_t frame_count = 0; + ifs_.read(reinterpret_cast(&frame_count), sizeof(frame_count)); + ifs_.seekg(2 * size, ::std::ios::cur); + if (prev_frame_count > frame_count) { + ifs_.seekg(-static_cast(sizeof(frame_count)) - 2 * size, ::std::ios::cur); + offset_frame_count_ = frame_count; + break; + } + prev_frame_count = frame_count; + ifs_.peek(); + + if (ifs_.eof()) { + open_and_check(width_, height_, output_directory_, file_idx_, ifs_, &finished_); + if (finished_) { + // Seek to first file and set offset when We cannot find base frame. + file_idx_ = 0; open_and_check(width_, height_, output_directory_, file_idx_, ifs_, &finished_); - if (finished_) { - // Seek to first file and set offset when We cannot find base frame. - file_idx_ = 0; - open_and_check(width_, height_, output_directory_, file_idx_, ifs_, &finished_); - ifs_.read(reinterpret_cast(&offset_frame_count_), sizeof(offset_frame_count_)); - ifs_.seekg(-static_cast(sizeof(offset_frame_count_)), ::std::ios::cur); - finished_ = false; - read_count_ = offset_frame_count_; - latest_frame_count_ = read_count_ - 1; - break; - } + ifs_.read(reinterpret_cast(&offset_frame_count_), sizeof(offset_frame_count_)); + ifs_.seekg(-static_cast(sizeof(offset_frame_count_)), ::std::ios::cur); + finished_ = false; + read_count_ = offset_frame_count_; + latest_frame_count_ = read_count_ - 1; + break; } } - current_idx_ = file_idx_; } + current_idx_ = file_idx_; + } - void open_and_check(uint32_t width, uint32_t height, const std::filesystem::path output_directory, uint32_t& file_idx, ::std::ifstream& ifs, bool* finished) { - auto file_path = output_directory / ("raw-" + ::std::to_string(file_idx++) + ".bin"); - - ifs = ::std::ifstream(file_path, ::std::ios::binary); - if (ifs.fail()) { - *finished = true; - return; - } + void open_and_check(uint32_t width, uint32_t height, const std::filesystem::path output_directory, uint32_t& file_idx, ::std::ifstream& ifs, bool* finished) + { + auto file_path = output_directory / ("raw-" + ::std::to_string(file_idx++) + ".bin"); - // skip header (size is 512) - ifs.seekg(512, ::std::ios_base::beg); + ifs = ::std::ifstream(file_path, ::std::ios::binary); + if (ifs.fail()) { + *finished = true; + return; } - uint32_t width_; - uint32_t height_; - std::filesystem::path output_directory_; - uint32_t file_idx_; - ::std::vector latest_frame0_; - ::std::vector latest_frame1_; - uint32_t latest_frame_count_; - uint32_t offset_frame_count_; - uint32_t read_count_; - ::std::ifstream ifs_; - bool finished_; - - uint32_t current_idx_; - static ::std::unordered_map < ::std::string, std::unique_ptr> instances; // declares Writer::instance - }; - - ::std::unordered_map< ::std::string, std::unique_ptr> Reader::instances; // defines Writer::instance + // skip header (size is 512) + ifs.seekg(512, ::std::ios_base::beg); + } + + uint32_t width_; + uint32_t height_; + std::filesystem::path output_directory_; + uint32_t file_idx_; + ::std::vector latest_frame0_; + ::std::vector latest_frame1_; + uint32_t latest_frame_count_; + uint32_t offset_frame_count_; + uint32_t read_count_; + ::std::ifstream ifs_; + bool finished_; + + uint32_t current_idx_; + static ::std::unordered_map<::std::string, std::unique_ptr> instances; // declares Writer::instance +}; + +::std::unordered_map<::std::string, std::unique_ptr> Reader::instances; // defines Writer::instance } -extern "C" ION_EXPORT -int binaryloader(halide_buffer_t *session_id_buf, int width, int height, halide_buffer_t * output_directory_buf, - halide_buffer_t * out0, halide_buffer_t * out1) { +extern "C" ION_EXPORT int binaryloader(halide_buffer_t* session_id_buf, int width, int height, halide_buffer_t* output_directory_buf, + halide_buffer_t* out0, halide_buffer_t* out1) +{ try { const ::std::string session_id(reinterpret_cast(session_id_buf->host)); @@ -640,27 +647,24 @@ int binaryloader(halide_buffer_t *session_id_buf, int width, int height, halide_ out1->dim[1].min = 0; out1->dim[1].extent = height; } - } - else { - r.get(out0->host, out1->host, out0->size_in_bytes()); + } else { + r.get(out0->host, out1->host, out0->size_in_bytes()); } return 0; - } - catch (const ::std::exception& e) { + } catch (const ::std::exception& e) { ::std::cerr << e.what() << ::std::endl; return -1; - } - catch (...) { + } catch (...) { ::std::cerr << "Unknown error" << ::std::endl; return -1; } } ION_REGISTER_EXTERN(binaryloader); -extern "C" ION_EXPORT -int binaryloader_finished(halide_buffer_t* in0, halide_buffer_t* in1, halide_buffer_t *session_id_buf, int width, int height, - halide_buffer_t * output_directory_buf, - halide_buffer_t * finished, halide_buffer_t* bin_idx) { +extern "C" ION_EXPORT int binaryloader_finished(halide_buffer_t* in0, halide_buffer_t* in1, halide_buffer_t* session_id_buf, int width, int height, + halide_buffer_t* output_directory_buf, + halide_buffer_t* finished, halide_buffer_t* bin_idx) +{ try { if (in0->is_bounds_query() || in1->is_bounds_query()) { @@ -676,27 +680,24 @@ int binaryloader_finished(halide_buffer_t* in0, halide_buffer_t* in1, halide_buf in1->dim[1].min = 0; in1->dim[1].extent = height; } - } - else { + } else { const ::std::string session_id(reinterpret_cast(session_id_buf->host)); const ::std::string output_directory(reinterpret_cast(output_directory_buf->host)); auto& r(Reader::get_instance(session_id, width, height, output_directory)); auto finished_flag = r.get_finished(); - *reinterpret_cast(finished->host) = finished_flag; - *reinterpret_cast(bin_idx->host) = r.get_index(); - if (finished_flag) { - r.close(); - r.release_instance(session_id); - } + *reinterpret_cast(finished->host) = finished_flag; + *reinterpret_cast(bin_idx->host) = r.get_index(); + if (finished_flag) { + r.close(); + r.release_instance(session_id); + } } return 0; - } - catch (const ::std::exception& e) { + } catch (const ::std::exception& e) { ::std::cerr << e.what() << ::std::endl; return -1; - } - catch (...) { + } catch (...) { ::std::cerr << "Unknown error" << ::std::endl; return -1; } diff --git a/src/bb/image-io/rt_realsense.h b/src/bb/image-io/rt_realsense.h index 7c1b46e0..8d9770e8 100644 --- a/src/bb/image-io/rt_realsense.h +++ b/src/bb/image-io/rt_realsense.h @@ -12,227 +12,240 @@ namespace ion { namespace bb { -namespace image_io { - -class RealSense { - // NOTE: Copied from librealsense2/rt_sensor.h - typedef enum rs2_stream { - RS2_STREAM_ANY, - RS2_STREAM_DEPTH, /**< Native stream of depth data produced by RealSense device */ - RS2_STREAM_COLOR, /**< Native stream of color data captured by RealSense device */ - RS2_STREAM_INFRARED, /**< Native stream of infrared data captured by RealSense device */ - RS2_STREAM_FISHEYE, /**< Native stream of fish-eye (wide) data captured from the dedicate motion camera */ - RS2_STREAM_GYRO, /**< Native stream of gyroscope motion data produced by RealSense device */ - RS2_STREAM_ACCEL, /**< Native stream of accelerometer motion data produced by RealSense device */ - RS2_STREAM_GPIO, /**< Signals from external device connected through GPIO */ - RS2_STREAM_POSE, /**< 6 Degrees of Freedom pose data, calculated by RealSense device */ - RS2_STREAM_CONFIDENCE, /**< 4 bit per-pixel depth confidence level */ - RS2_STREAM_COUNT - } rs2_stream_t; - typedef enum rs2_format { - RS2_FORMAT_ANY, /**< When passed to enable stream, librealsense will try to provide best suited format */ - RS2_FORMAT_Z16, /**< 16-bit linear depth values. The depth is meters is equal to depth scale * pixel value. */ - RS2_FORMAT_DISPARITY16, /**< 16-bit float-point disparity values. Depth->Disparity conversion : Disparity = Baseline*FocalLength/Depth. */ - RS2_FORMAT_XYZ32F, /**< 32-bit floating point 3D coordinates. */ - RS2_FORMAT_YUYV, /**< 32-bit y0, u, y1, v data for every two pixels. Similar to YUV422 but packed in a different order - https://en.wikipedia.org/wiki/YUV */ - RS2_FORMAT_RGB8, /**< 8-bit red, green and blue channels */ - RS2_FORMAT_BGR8, /**< 8-bit blue, green, and red channels -- suitable for OpenCV */ - RS2_FORMAT_RGBA8, /**< 8-bit red, green and blue channels + constant alpha channel equal to FF */ - RS2_FORMAT_BGRA8, /**< 8-bit blue, green, and red channels + constant alpha channel equal to FF */ - RS2_FORMAT_Y8, /**< 8-bit per-pixel grayscale image */ - RS2_FORMAT_Y16, /**< 16-bit per-pixel grayscale image */ - RS2_FORMAT_RAW10, /**< Four 10 bits per pixel luminance values packed into a 5-byte macropixel */ - RS2_FORMAT_RAW16, /**< 16-bit raw image */ - RS2_FORMAT_RAW8, /**< 8-bit raw image */ - RS2_FORMAT_UYVY, /**< Similar to the standard YUYV pixel format, but packed in a different order */ - RS2_FORMAT_MOTION_RAW, /**< Raw data from the motion sensor */ - RS2_FORMAT_MOTION_XYZ32F, /**< Motion data packed as 3 32-bit float values, for X, Y, and Z axis */ - RS2_FORMAT_GPIO_RAW, /**< Raw data from the external sensors hooked to one of the GPIO's */ - RS2_FORMAT_6DOF, /**< Pose data packed as floats array, containing translation vector, rotation quaternion and prediction velocities and accelerations vectors */ - RS2_FORMAT_DISPARITY32, /**< 32-bit float-point disparity values. Depth->Disparity conversion : Disparity = Baseline*FocalLength/Depth */ - RS2_FORMAT_Y10BPACK, /**< 16-bit per-pixel grayscale image unpacked from 10 bits per pixel packed ([8:8:8:8:2222]) grey-scale image. The data is unpacked to LSB and padded with 6 zero bits */ - RS2_FORMAT_DISTANCE, /**< 32-bit float-point depth distance value. */ - RS2_FORMAT_MJPEG, /**< Bitstream encoding for video in which an image of each frame is encoded as JPEG-DIB */ - RS2_FORMAT_Y8I, /**< 8-bit per pixel interleaved. 8-bit left, 8-bit right. */ - RS2_FORMAT_Y12I, /**< 12-bit per pixel interleaved. 12-bit left, 12-bit right. Each pixel is stored in a 24-bit word in little-endian order. */ - RS2_FORMAT_INZI, /**< multi-planar Depth 16bit + IR 10bit. */ - RS2_FORMAT_INVI, /**< 8-bit IR stream. */ - RS2_FORMAT_W10, /**< Grey-scale image as a bit-packed array. 4 pixel data stream taking 5 bytes */ - RS2_FORMAT_Z16H, /**< Variable-length Huffman-compressed 16-bit depth values. */ - RS2_FORMAT_COUNT /**< Number of enumeration values. Not a valid input: intended to be used in for-loops. */ - } rs2_format_t; - - using rs2_error_t = struct rs2_error; - using rs2_context_t = struct rs2_context; - using rs2_device_list_t = struct rs2_device_list; - using rs2_pipeline_t = struct rs2_pipeline; - using rs2_pipeline_profile_t = struct rs2_pipeline_profile; - using rs2_config_t = struct rs2_config; - using rs2_frame_t = struct rs2_frame; - - using rs2_get_api_version_t = int (*)(rs2_error_t **error); - using rs2_get_error_message_t = const char *(*)(const rs2_error_t *); - using rs2_create_context_t = rs2_context_t *(*)(int, rs2_error_t **); - using rs2_query_devices_t = rs2_device_list_t *(*)(const rs2_context_t *, rs2_error_t **); - using rs2_get_device_count_t = int (*)(const rs2_device_list_t *, rs2_error_t **); - using rs2_create_config_t = rs2_config_t *(*)(rs2_error_t **); - using rs2_config_enable_stream_t = void (*)(rs2_config_t *, rs2_stream_t, int, int, int, rs2_format_t, int, rs2_error_t **); - using rs2_create_pipeline_t = rs2_pipeline_t *(*)(rs2_context_t *, rs2_error_t **); - using rs2_pipeline_start_with_config_t = rs2_pipeline_profile_t *(*)(rs2_pipeline_t *, rs2_config_t *, rs2_error_t **); - using rs2_pipeline_stop_t = void (*)(rs2_pipeline_t *, rs2_error_t **); - using rs2_pipeline_wait_for_frames_t = rs2_frame_t *(*)(rs2_pipeline_t *, unsigned int, rs2_error_t **); - using rs2_extract_frame_t = rs2_frame_t *(*)(rs2_frame_t *, int, rs2_error_t **); - using rs2_get_frame_data_t = const void *(*)(const rs2_frame_t *, rs2_error_t **); - using rs2_release_frame_t = void (*)(const rs2_frame_t *); - - rs2_get_api_version_t rs2_get_api_version; - rs2_get_error_message_t rs2_get_error_message; - rs2_create_context_t rs2_create_context; - rs2_query_devices_t rs2_query_devices; - rs2_get_device_count_t rs2_get_device_count; - rs2_create_config_t rs2_create_config; - rs2_config_enable_stream_t rs2_config_enable_stream; - rs2_create_pipeline_t rs2_create_pipeline; - rs2_pipeline_start_with_config_t rs2_pipeline_start_with_config; - rs2_pipeline_stop_t rs2_pipeline_stop; - rs2_pipeline_wait_for_frames_t rs2_pipeline_wait_for_frames; - rs2_extract_frame_t rs2_extract_frame; - rs2_get_frame_data_t rs2_get_frame_data; - rs2_release_frame_t rs2_release_frame; - -public: - static RealSense &get_instance(int32_t width, int32_t height) { - static RealSense instance(width, height); - return instance; - } - - RealSense(int32_t width, int32_t height) - : dm_("realsense2", false), device_is_available_(false), ctx_(nullptr), devices_(nullptr), pipeline_(nullptr), config_(nullptr), frameset_(nullptr) { + namespace image_io { + + class RealSense { + // NOTE: Copied from librealsense2/rt_sensor.h + typedef enum rs2_stream { + RS2_STREAM_ANY, + RS2_STREAM_DEPTH, /**< Native stream of depth data produced by RealSense device */ + RS2_STREAM_COLOR, /**< Native stream of color data captured by RealSense device */ + RS2_STREAM_INFRARED, /**< Native stream of infrared data captured by RealSense device */ + RS2_STREAM_FISHEYE, /**< Native stream of fish-eye (wide) data captured from the dedicate motion camera */ + RS2_STREAM_GYRO, /**< Native stream of gyroscope motion data produced by RealSense device */ + RS2_STREAM_ACCEL, /**< Native stream of accelerometer motion data produced by RealSense device */ + RS2_STREAM_GPIO, /**< Signals from external device connected through GPIO */ + RS2_STREAM_POSE, /**< 6 Degrees of Freedom pose data, calculated by RealSense device */ + RS2_STREAM_CONFIDENCE, /**< 4 bit per-pixel depth confidence level */ + RS2_STREAM_COUNT + } rs2_stream_t; + typedef enum rs2_format { + RS2_FORMAT_ANY, /**< When passed to enable stream, librealsense will try to provide best suited format */ + RS2_FORMAT_Z16, /**< 16-bit linear depth values. The depth is meters is equal to depth scale * pixel value. */ + RS2_FORMAT_DISPARITY16, /**< 16-bit float-point disparity values. Depth->Disparity conversion : Disparity = Baseline*FocalLength/Depth. */ + RS2_FORMAT_XYZ32F, /**< 32-bit floating point 3D coordinates. */ + RS2_FORMAT_YUYV, /**< 32-bit y0, u, y1, v data for every two pixels. Similar to YUV422 but packed in a different order - https://en.wikipedia.org/wiki/YUV */ + RS2_FORMAT_RGB8, /**< 8-bit red, green and blue channels */ + RS2_FORMAT_BGR8, /**< 8-bit blue, green, and red channels -- suitable for OpenCV */ + RS2_FORMAT_RGBA8, /**< 8-bit red, green and blue channels + constant alpha channel equal to FF */ + RS2_FORMAT_BGRA8, /**< 8-bit blue, green, and red channels + constant alpha channel equal to FF */ + RS2_FORMAT_Y8, /**< 8-bit per-pixel grayscale image */ + RS2_FORMAT_Y16, /**< 16-bit per-pixel grayscale image */ + RS2_FORMAT_RAW10, /**< Four 10 bits per pixel luminance values packed into a 5-byte macropixel */ + RS2_FORMAT_RAW16, /**< 16-bit raw image */ + RS2_FORMAT_RAW8, /**< 8-bit raw image */ + RS2_FORMAT_UYVY, /**< Similar to the standard YUYV pixel format, but packed in a different order */ + RS2_FORMAT_MOTION_RAW, /**< Raw data from the motion sensor */ + RS2_FORMAT_MOTION_XYZ32F, /**< Motion data packed as 3 32-bit float values, for X, Y, and Z axis */ + RS2_FORMAT_GPIO_RAW, /**< Raw data from the external sensors hooked to one of the GPIO's */ + RS2_FORMAT_6DOF, /**< Pose data packed as floats array, containing translation vector, rotation quaternion and prediction velocities and accelerations vectors */ + RS2_FORMAT_DISPARITY32, /**< 32-bit float-point disparity values. Depth->Disparity conversion : Disparity = Baseline*FocalLength/Depth */ + RS2_FORMAT_Y10BPACK, /**< 16-bit per-pixel grayscale image unpacked from 10 bits per pixel packed ([8:8:8:8:2222]) grey-scale image. The data is unpacked to LSB and padded with 6 zero bits */ + RS2_FORMAT_DISTANCE, /**< 32-bit float-point depth distance value. */ + RS2_FORMAT_MJPEG, /**< Bitstream encoding for video in which an image of each frame is encoded as JPEG-DIB */ + RS2_FORMAT_Y8I, /**< 8-bit per pixel interleaved. 8-bit left, 8-bit right. */ + RS2_FORMAT_Y12I, /**< 12-bit per pixel interleaved. 12-bit left, 12-bit right. Each pixel is stored in a 24-bit word in little-endian order. */ + RS2_FORMAT_INZI, /**< multi-planar Depth 16bit + IR 10bit. */ + RS2_FORMAT_INVI, /**< 8-bit IR stream. */ + RS2_FORMAT_W10, /**< Grey-scale image as a bit-packed array. 4 pixel data stream taking 5 bytes */ + RS2_FORMAT_Z16H, /**< Variable-length Huffman-compressed 16-bit depth values. */ + RS2_FORMAT_COUNT /**< Number of enumeration values. Not a valid input: intended to be used in for-loops. */ + } rs2_format_t; + + using rs2_error_t = struct rs2_error; + using rs2_context_t = struct rs2_context; + using rs2_device_list_t = struct rs2_device_list; + using rs2_pipeline_t = struct rs2_pipeline; + using rs2_pipeline_profile_t = struct rs2_pipeline_profile; + using rs2_config_t = struct rs2_config; + using rs2_frame_t = struct rs2_frame; + + using rs2_get_api_version_t = int (*)(rs2_error_t** error); + using rs2_get_error_message_t = const char* (*)(const rs2_error_t*); + using rs2_create_context_t = rs2_context_t* (*)(int, rs2_error_t**); + using rs2_query_devices_t = rs2_device_list_t* (*)(const rs2_context_t*, rs2_error_t**); + using rs2_get_device_count_t = int (*)(const rs2_device_list_t*, rs2_error_t**); + using rs2_create_config_t = rs2_config_t* (*)(rs2_error_t**); + using rs2_config_enable_stream_t = void (*)(rs2_config_t*, rs2_stream_t, int, int, int, rs2_format_t, int, rs2_error_t**); + using rs2_create_pipeline_t = rs2_pipeline_t* (*)(rs2_context_t*, rs2_error_t**); + using rs2_pipeline_start_with_config_t = rs2_pipeline_profile_t* (*)(rs2_pipeline_t*, rs2_config_t*, rs2_error_t**); + using rs2_pipeline_stop_t = void (*)(rs2_pipeline_t*, rs2_error_t**); + using rs2_pipeline_wait_for_frames_t = rs2_frame_t* (*)(rs2_pipeline_t*, unsigned int, rs2_error_t**); + using rs2_extract_frame_t = rs2_frame_t* (*)(rs2_frame_t*, int, rs2_error_t**); + using rs2_get_frame_data_t = const void* (*)(const rs2_frame_t*, rs2_error_t**); + using rs2_release_frame_t = void (*)(const rs2_frame_t*); + + rs2_get_api_version_t rs2_get_api_version; + rs2_get_error_message_t rs2_get_error_message; + rs2_create_context_t rs2_create_context; + rs2_query_devices_t rs2_query_devices; + rs2_get_device_count_t rs2_get_device_count; + rs2_create_config_t rs2_create_config; + rs2_config_enable_stream_t rs2_config_enable_stream; + rs2_create_pipeline_t rs2_create_pipeline; + rs2_pipeline_start_with_config_t rs2_pipeline_start_with_config; + rs2_pipeline_stop_t rs2_pipeline_stop; + rs2_pipeline_wait_for_frames_t rs2_pipeline_wait_for_frames; + rs2_extract_frame_t rs2_extract_frame; + rs2_get_frame_data_t rs2_get_frame_data; + rs2_release_frame_t rs2_release_frame; + + public: + static RealSense& get_instance(int32_t width, int32_t height) + { + static RealSense instance(width, height); + return instance; + } - if (!init_symbols()) { - log::error("Failed to init realsense symbols"); - return; - } + RealSense(int32_t width, int32_t height) + : dm_("realsense2", false) + , device_is_available_(false) + , ctx_(nullptr) + , devices_(nullptr) + , pipeline_(nullptr) + , config_(nullptr) + , frameset_(nullptr) + { + + if (!init_symbols()) { + log::error("Failed to init realsense symbols"); + return; + } - rs2_error_t *err = nullptr; + rs2_error_t* err = nullptr; - int version = rs2_get_api_version(&err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + int version = rs2_get_api_version(&err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - ctx_ = rs2_create_context(version, &err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + ctx_ = rs2_create_context(version, &err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - devices_ = rs2_query_devices(ctx_, &err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + devices_ = rs2_query_devices(ctx_, &err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - int num_of_devices = rs2_get_device_count(devices_, &err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } - if (num_of_devices == 0) { - log::error("Cannot find realsense device"); - return; - } + int num_of_devices = rs2_get_device_count(devices_, &err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } + if (num_of_devices == 0) { + log::error("Cannot find realsense device"); + return; + } - int fps = 30; - config_ = rs2_create_config(&err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + int fps = 30; + config_ = rs2_create_config(&err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - rs2_config_enable_stream(config_, RS2_STREAM_INFRARED, 1, width, height, RS2_FORMAT_Y8, fps, &err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + rs2_config_enable_stream(config_, RS2_STREAM_INFRARED, 1, width, height, RS2_FORMAT_Y8, fps, &err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - rs2_config_enable_stream(config_, RS2_STREAM_INFRARED, 2, width, height, RS2_FORMAT_Y8, fps, &err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + rs2_config_enable_stream(config_, RS2_STREAM_INFRARED, 2, width, height, RS2_FORMAT_Y8, fps, &err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - rs2_config_enable_stream(config_, RS2_STREAM_DEPTH, 0, width, height, RS2_FORMAT_Z16, fps, &err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + rs2_config_enable_stream(config_, RS2_STREAM_DEPTH, 0, width, height, RS2_FORMAT_Z16, fps, &err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - rs2_config_enable_stream(config_, RS2_STREAM_DEPTH, 0, width, height, RS2_FORMAT_Z16, fps, &err); - if (err) { - return; - } + rs2_config_enable_stream(config_, RS2_STREAM_DEPTH, 0, width, height, RS2_FORMAT_Z16, fps, &err); + if (err) { + return; + } - pipeline_ = rs2_create_pipeline(ctx_, &err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + pipeline_ = rs2_create_pipeline(ctx_, &err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - rs2_pipeline_start_with_config(pipeline_, config_, &err); - if (err) { - log::error("Failed call realsense API: {}", rs2_get_error_message(err)); - return; - } + rs2_pipeline_start_with_config(pipeline_, config_, &err); + if (err) { + log::error("Failed call realsense API: {}", rs2_get_error_message(err)); + return; + } - device_is_available_ = true; - } + device_is_available_ = true; + } - ~RealSense() { - rs2_error_t *err = nullptr; - if (init_symbols()) { - rs2_pipeline_stop(pipeline_, &err); - } - // TODO: Delete recources - } + ~RealSense() + { + rs2_error_t* err = nullptr; + if (init_symbols()) { + rs2_pipeline_stop(pipeline_, &err); + } + // TODO: Delete recources + } - void get_frameset(Halide::Runtime::Buffer &buf) { - rs2_error_t *err = nullptr; - if (frameset_) { - rs2_release_frame(frameset_); - } - frameset_ = rs2_pipeline_wait_for_frames(pipeline_, 15000, &err); - if (err) { - throw std::runtime_error(rs2_get_error_message(err)); - } - buf() = reinterpret_cast(frameset_); - } + void get_frameset(Halide::Runtime::Buffer& buf) + { + rs2_error_t* err = nullptr; + if (frameset_) { + rs2_release_frame(frameset_); + } + frameset_ = rs2_pipeline_wait_for_frames(pipeline_, 15000, &err); + if (err) { + throw std::runtime_error(rs2_get_error_message(err)); + } + buf() = reinterpret_cast(frameset_); + } - const void *get_frame_ptr(void *frameset, int index) { - rs2_error_t *err = nullptr; - rs2_frame_t *frame = rs2_extract_frame(static_cast(frameset), index, &err); - if (err) { - throw std::runtime_error(rs2_get_error_message(err)); - } + const void* get_frame_ptr(void* frameset, int index) + { + rs2_error_t* err = nullptr; + rs2_frame_t* frame = rs2_extract_frame(static_cast(frameset), index, &err); + if (err) { + throw std::runtime_error(rs2_get_error_message(err)); + } - const void *frame_ptr = rs2_get_frame_data(frame, &err); - if (err) { - throw std::runtime_error(rs2_get_error_message(err)); - } + const void* frame_ptr = rs2_get_frame_data(frame, &err); + if (err) { + throw std::runtime_error(rs2_get_error_message(err)); + } - return frame_ptr; - } + return frame_ptr; + } - bool is_available() const { - return device_is_available_; - } + bool is_available() const + { + return device_is_available_; + } -private: - bool init_symbols() { - if (!dm_.is_available()) { - return false; - } + private: + bool init_symbols() + { + if (!dm_.is_available()) { + return false; + } #define GET_SYMBOL(LOCAL_VAR, TARGET_SYMBOL) \ LOCAL_VAR = dm_.get_symbol(TARGET_SYMBOL); \ @@ -240,46 +253,47 @@ class RealSense { return false; \ } - GET_SYMBOL(rs2_get_api_version, "rs2_get_api_version"); - GET_SYMBOL(rs2_get_error_message, "rs2_get_error_message"); - GET_SYMBOL(rs2_create_context, "rs2_create_context"); - GET_SYMBOL(rs2_query_devices, "rs2_query_devices"); - GET_SYMBOL(rs2_get_device_count, "rs2_get_device_count"); - GET_SYMBOL(rs2_create_config, "rs2_create_config"); - GET_SYMBOL(rs2_config_enable_stream, "rs2_config_enable_stream"); - GET_SYMBOL(rs2_create_pipeline, "rs2_create_pipeline"); - GET_SYMBOL(rs2_pipeline_start_with_config, "rs2_pipeline_start_with_config"); - GET_SYMBOL(rs2_pipeline_stop, "rs2_pipeline_stop"); - GET_SYMBOL(rs2_pipeline_wait_for_frames, "rs2_pipeline_wait_for_frames"); - GET_SYMBOL(rs2_extract_frame, "rs2_extract_frame"); - GET_SYMBOL(rs2_get_frame_data, "rs2_get_frame_data"); - GET_SYMBOL(rs2_release_frame, "rs2_release_frame"); + GET_SYMBOL(rs2_get_api_version, "rs2_get_api_version"); + GET_SYMBOL(rs2_get_error_message, "rs2_get_error_message"); + GET_SYMBOL(rs2_create_context, "rs2_create_context"); + GET_SYMBOL(rs2_query_devices, "rs2_query_devices"); + GET_SYMBOL(rs2_get_device_count, "rs2_get_device_count"); + GET_SYMBOL(rs2_create_config, "rs2_create_config"); + GET_SYMBOL(rs2_config_enable_stream, "rs2_config_enable_stream"); + GET_SYMBOL(rs2_create_pipeline, "rs2_create_pipeline"); + GET_SYMBOL(rs2_pipeline_start_with_config, "rs2_pipeline_start_with_config"); + GET_SYMBOL(rs2_pipeline_stop, "rs2_pipeline_stop"); + GET_SYMBOL(rs2_pipeline_wait_for_frames, "rs2_pipeline_wait_for_frames"); + GET_SYMBOL(rs2_extract_frame, "rs2_extract_frame"); + GET_SYMBOL(rs2_get_frame_data, "rs2_get_frame_data"); + GET_SYMBOL(rs2_release_frame, "rs2_release_frame"); #undef GET_SYMBOL - return true; - } + return true; + } - DynamicModule dm_; - bool device_is_available_; + DynamicModule dm_; + bool device_is_available_; - rs2_context_t *ctx_; - rs2_device_list_t *devices_; - rs2_pipeline_t *pipeline_; - rs2_config_t *config_; - rs2_frame_t *frameset_; -}; + rs2_context_t* ctx_; + rs2_device_list_t* devices_; + rs2_pipeline_t* pipeline_; + rs2_config_t* config_; + rs2_frame_t* frameset_; + }; -} // namespace image_io -} // namespace bb -} // namespace ion + } // namespace image_io +} // namespace bb +} // namespace ion -extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_infrared(halide_buffer_t *in, halide_buffer_t *out_l, halide_buffer_t *out_r) { +extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_infrared(halide_buffer_t* in, halide_buffer_t* out_l, halide_buffer_t* out_r) +{ try { const int width = 1280; const int height = 720; - auto &realsense(ion::bb::image_io::RealSense::get_instance(width, height)); + auto& realsense(ion::bb::image_io::RealSense::get_instance(width, height)); if (out_l->is_bounds_query() || out_r->is_bounds_query()) { if (out_l->is_bounds_query()) { @@ -298,7 +312,7 @@ extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_infrared(halide_buffer_ Halide::Runtime::Buffer obuf_l(*out_l); Halide::Runtime::Buffer obuf_r(*out_r); - void *frameset = reinterpret_cast(Halide::Runtime::Buffer(*in)()); + void* frameset = reinterpret_cast(Halide::Runtime::Buffer(*in)()); if (frameset) { std::memcpy(obuf_l.data(), realsense.get_frame_ptr(frameset, 1), obuf_l.size_in_bytes()); std::memcpy(obuf_r.data(), realsense.get_frame_ptr(frameset, 2), obuf_r.size_in_bytes()); @@ -313,7 +327,7 @@ extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_infrared(halide_buffer_ } } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -324,12 +338,13 @@ extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_infrared(halide_buffer_ ION_REGISTER_EXTERN(ion_bb_image_io_realsense_d435_infrared) -extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_depth(halide_buffer_t *in, halide_buffer_t *out_d) { +extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_depth(halide_buffer_t* in, halide_buffer_t* out_d) +{ try { const int width = 1280; const int height = 720; - auto &realsense(ion::bb::image_io::RealSense::get_instance(width, height)); + auto& realsense(ion::bb::image_io::RealSense::get_instance(width, height)); if (out_d->is_bounds_query()) { if (out_d->is_bounds_query()) { @@ -340,7 +355,7 @@ extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_depth(halide_buffer_t * } } else { Halide::Runtime::Buffer obuf_d(*out_d); - void *frameset = reinterpret_cast(Halide::Runtime::Buffer(*in)()); + void* frameset = reinterpret_cast(Halide::Runtime::Buffer(*in)()); if (frameset) { std::memcpy(obuf_d.data(), realsense.get_frame_ptr(frameset, 0), obuf_d.size_in_bytes()); } else { @@ -353,7 +368,7 @@ extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_depth(halide_buffer_t * } } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -363,12 +378,13 @@ extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_depth(halide_buffer_t * } ION_REGISTER_EXTERN(ion_bb_image_io_realsense_d435_depth) -extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_frameset(halide_buffer_t *out) { +extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_frameset(halide_buffer_t* out) +{ try { const int width = 1280; const int height = 720; - auto &realsense(ion::bb::image_io::RealSense::get_instance(width, height)); + auto& realsense(ion::bb::image_io::RealSense::get_instance(width, height)); if (out->is_bounds_query()) { // out->dim[0].min = 0; @@ -382,7 +398,7 @@ extern "C" int ION_EXPORT ion_bb_image_io_realsense_d435_frameset(halide_buffer_ } } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { diff --git a/src/bb/image-io/rt_u3v.h b/src/bb/image-io/rt_u3v.h index 9f758256..f1e8e172 100644 --- a/src/bb/image-io/rt_u3v.h +++ b/src/bb/image-io/rt_u3v.h @@ -9,1386 +9,1399 @@ #include "log.h" -#include "rt_common.h" #include "gendc_separator/ContainerHeader.h" #include "gendc_separator/tools.h" +#include "rt_common.h" #define ComponentIDIntensity 1 #ifdef _WIN32 - #define GOBJECT_FILE "gobject-2.0-0" - #define ARAVIS_FILE "aravis-0.8-0" +#define GOBJECT_FILE "gobject-2.0-0" +#define ARAVIS_FILE "aravis-0.8-0" #else - #define GOBJECT_FILE "gobject-2.0" - #define ARAVIS_FILE "aravis-0.8" +#define GOBJECT_FILE "gobject-2.0" +#define ARAVIS_FILE "aravis-0.8" #endif namespace ion { namespace bb { -namespace image_io { - -class U3V { -protected: - struct GError - { - uint32_t domain; - int32_t code; - const char *message; - }; - - enum OperationMode - { Came2USB1, - Came1USB1, - Came2USB2, - Came1USB2 - }; - - enum FrameCountMethod { - UNAVAILABLE = -1, - TIMESTAMP = 0, - TYPESPECIFIC3 = 1 - }; - - using gpointer = struct gpointer_*; - - using g_object_unref_t = void (*)(gpointer); - - typedef enum ArvAcquisitionMode{ - ARV_ACQUISITION_MODE_CONTINUOUS, - ARV_ACQUISITION_MODE_SINGLE_FRAME - } ArvAcquisitionMode_t; - - typedef enum ArvBufferStatus{ - ARV_BUFFER_STATUS_UNKNOWN, - ARV_BUFFER_STATUS_SUCCESS, - ARV_BUFFER_STATUS_CLEARED, - ARV_BUFFER_STATUS_TIMEOUT, - ARV_BUFFER_STATUS_MISSING_PACKETS, - ARV_BUFFER_STATUS_WRONG_PACKET_ID, - ARV_BUFFER_STATUS_SIZE_MISMATCH, - ARV_BUFFER_STATUS_FILLING, - ARV_BUFFER_STATUS_ABORTED - } ArvBufferStatus_t; - - typedef enum ArvBufferPayloadType{ - ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN, - ARV_BUFFER_PAYLOAD_TYPE_IMAGE, - ARV_BUFFER_PAYLOAD_TYPE_RAWDATA, - ARV_BUFFER_PAYLOAD_TYPE_FILE, - ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA, - ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA, - ARV_BUFFER_PAYLOAD_TYPE_JPEG, - ARV_BUFFER_PAYLOAD_TYPE_JPEG2000, - ARV_BUFFER_PAYLOAD_TYPE_H264, - ARV_BUFFER_PAYLOAD_TYPE_MULTIZONE_IMAGE - }ArvBufferPayloadType_t; - - typedef enum ArvDeviceStatus{ - ARV_DEVICE_STATUS_UNKNOWN, - ARV_DEVICE_STATUS_SUCCESS, - ARV_DEVICE_STATUS_TIMEOUT, - ARV_DEVICE_STATUS_WRITE_ERROR - }ArvDeviceStatus_t; - - typedef enum ArvUvUsbMode{ - ARV_UV_USB_MODE_SYNC, - ARV_UV_USB_MODE_ASYNC, - ARV_UV_USB_MODE_DEFAULT = ARV_UV_USB_MODE_ASYNC - } ArvUvUsbMode_t; - - using ArvDevice_t = struct ArvDevice*; - using ArvFakeDevice_t = struct ArvFakeDevice*; - using ArvStream_t = struct ArvStream*; - using ArvStreamCallback_t = struct ArvStreamCallback*; - using ArvBuffer_t = struct ArvBuffer*; - using ArvGcNode_t = struct ArvGcNode*; - using ArvCamera_t = struct ArvCamera*; - - using arv_get_device_protocol_t = const char*(*)(unsigned int); - - using arv_get_major_version_t = uint32_t(*)(); - using arv_get_minor_version_t = uint32_t(*)(); - using arv_get_micro_version_t = uint32_t(*)(); - - using arv_update_device_list_t = void(*)(); - using arv_get_n_devices_t = unsigned int(*)(); - - using arv_get_device_id_t = const char*(*)(unsigned int); - using arv_get_device_model_t = const char*(*)(unsigned int); - using arv_get_device_serial_nbr_t = const char*(*)(unsigned int); - using arv_open_device_t = ArvDevice*(*)(const char*, GError**); - - using arv_device_set_string_feature_value_t = void(*)(ArvDevice*, const char*, const char*, GError**); - using arv_device_set_float_feature_value_t = void(*)(ArvDevice*, const char*, double, GError**); - using arv_device_set_integer_feature_value_t = void(*)(ArvDevice*, const char*, int64_t, GError**); - - using arv_device_get_string_feature_value_t = const char *(*)(ArvDevice*, const char*, GError**); - using arv_device_get_integer_feature_value_t = int(*)(ArvDevice*, const char*, GError**); - using arv_device_get_float_feature_value_t = double(*)(ArvDevice*, const char*, GError**); - - using arv_device_get_integer_feature_bounds_t = void(*)(ArvDevice*, const char*, int64_t*, int64_t*, GError**); - using arv_device_get_float_feature_bounds_t = void(*)(ArvDevice*, const char*, double*, double*, GError**); - - using arv_device_is_feature_available_t = bool(*)(ArvDevice*, const char*, GError**); - - using arv_device_dup_register_feature_value_t = void*(*) (ArvDevice*, const char *, uint64_t *, GError **); - - using arv_device_create_stream_t = ArvStream*(*)(ArvDevice*, ArvStreamCallback*, void*, GError**); - - using arv_buffer_new_allocate_t = ArvBuffer*(*)(size_t); - using arv_stream_push_buffer_t = void(*)(ArvStream*, ArvBuffer*); - - using arv_acquisition_mode_to_string_t = const char*(*)(ArvAcquisitionMode); - using arv_device_execute_command_t = void(*)(ArvDevice*, const char*, GError**); - using arv_stream_timeout_pop_buffer_t = ArvBuffer*(*)(ArvStream*, uint64_t); - using arv_stream_get_n_buffers_t = void(*)(ArvStream*, int32_t*, int32_t*); - using arv_buffer_get_status_t = ArvBufferStatus(*)(ArvBuffer*); - using arv_buffer_get_payload_type_t = ArvBufferPayloadType(*)(ArvBuffer*); - using arv_buffer_get_data_t = void*(*)(ArvBuffer*, size_t*); - using arv_buffer_get_part_data_t = void*(*)(ArvBuffer*, uint_fast32_t, size_t*); - using arv_buffer_get_timestamp_t = uint64_t(*)(ArvBuffer*); - using arv_device_get_feature_t = ArvGcNode*(*)(ArvDevice*, const char*); - - using arv_buffer_has_gendc_t = bool*(*)(ArvBuffer*); - using arv_buffer_get_gendc_descriptor_t = void*(*)(ArvBuffer*, size_t*); - - using arv_shutdown_t = void(*)(void); - - using arv_camera_new_t = ArvCamera*(*)(const char*, GError**); - using arv_camera_get_device_t = ArvDevice*(*)(ArvCamera *); - using arv_fake_device_new_t = ArvDevice*(*)(const char*, GError**); - using arv_set_fake_camera_genicam_filename_t = void(*)(const char*); - using arv_enable_interface_t = void(*)(const char*); - using arv_camera_create_stream_t = ArvStream*(*)(ArvCamera*, ArvStreamCallback*, void*, GError**); - using arv_fake_device_get_fake_camera_t = ArvCamera*(*)(ArvFakeDevice*); - - using arv_uv_device_set_usb_mode_t = void(*)(ArvDevice *, ArvUvUsbMode ); - - struct DeviceInfo { - const char* dev_id_; - ArvDevice* device_; - ArvCamera* camera_; - - int32_t u3v_payload_size_; - int32_t image_payload_size_; - uint32_t frame_count_; - - float gain_ =-1; - float exposure_ =-1; - - int32_t int_gain_ = -1; - int32_t int_exposure_ = -1; - - float exposure_range_[2]; - - ArvStream* stream_; - - // genDC - int64_t data_offset_; - std::tuple available_comp_part; - int32_t framecount_offset_; - bool is_data_image_; + namespace image_io { + + class U3V { + protected: + struct GError { + uint32_t domain; + int32_t code; + const char* message; + }; + + enum OperationMode { Came2USB1, + Came1USB1, + Came2USB2, + Came1USB2 + }; + + enum FrameCountMethod { + UNAVAILABLE = -1, + TIMESTAMP = 0, + TYPESPECIFIC3 = 1 + }; + + using gpointer = struct gpointer_*; + + using g_object_unref_t = void (*)(gpointer); + + typedef enum ArvAcquisitionMode { + ARV_ACQUISITION_MODE_CONTINUOUS, + ARV_ACQUISITION_MODE_SINGLE_FRAME + } ArvAcquisitionMode_t; + + typedef enum ArvBufferStatus { + ARV_BUFFER_STATUS_UNKNOWN, + ARV_BUFFER_STATUS_SUCCESS, + ARV_BUFFER_STATUS_CLEARED, + ARV_BUFFER_STATUS_TIMEOUT, + ARV_BUFFER_STATUS_MISSING_PACKETS, + ARV_BUFFER_STATUS_WRONG_PACKET_ID, + ARV_BUFFER_STATUS_SIZE_MISMATCH, + ARV_BUFFER_STATUS_FILLING, + ARV_BUFFER_STATUS_ABORTED + } ArvBufferStatus_t; + + typedef enum ArvBufferPayloadType { + ARV_BUFFER_PAYLOAD_TYPE_UNKNOWN, + ARV_BUFFER_PAYLOAD_TYPE_IMAGE, + ARV_BUFFER_PAYLOAD_TYPE_RAWDATA, + ARV_BUFFER_PAYLOAD_TYPE_FILE, + ARV_BUFFER_PAYLOAD_TYPE_CHUNK_DATA, + ARV_BUFFER_PAYLOAD_TYPE_EXTENDED_CHUNK_DATA, + ARV_BUFFER_PAYLOAD_TYPE_JPEG, + ARV_BUFFER_PAYLOAD_TYPE_JPEG2000, + ARV_BUFFER_PAYLOAD_TYPE_H264, + ARV_BUFFER_PAYLOAD_TYPE_MULTIZONE_IMAGE + } ArvBufferPayloadType_t; + + typedef enum ArvDeviceStatus { + ARV_DEVICE_STATUS_UNKNOWN, + ARV_DEVICE_STATUS_SUCCESS, + ARV_DEVICE_STATUS_TIMEOUT, + ARV_DEVICE_STATUS_WRITE_ERROR + } ArvDeviceStatus_t; + + typedef enum ArvUvUsbMode { + ARV_UV_USB_MODE_SYNC, + ARV_UV_USB_MODE_ASYNC, + ARV_UV_USB_MODE_DEFAULT = ARV_UV_USB_MODE_ASYNC + } ArvUvUsbMode_t; + + using ArvDevice_t = struct ArvDevice*; + using ArvFakeDevice_t = struct ArvFakeDevice*; + using ArvStream_t = struct ArvStream*; + using ArvStreamCallback_t = struct ArvStreamCallback*; + using ArvBuffer_t = struct ArvBuffer*; + using ArvGcNode_t = struct ArvGcNode*; + using ArvCamera_t = struct ArvCamera*; + + using arv_get_device_protocol_t = const char* (*)(unsigned int); + + using arv_get_major_version_t = uint32_t (*)(); + using arv_get_minor_version_t = uint32_t (*)(); + using arv_get_micro_version_t = uint32_t (*)(); + + using arv_update_device_list_t = void (*)(); + using arv_get_n_devices_t = unsigned int (*)(); + + using arv_get_device_id_t = const char* (*)(unsigned int); + using arv_get_device_model_t = const char* (*)(unsigned int); + using arv_get_device_serial_nbr_t = const char* (*)(unsigned int); + using arv_open_device_t = ArvDevice* (*)(const char*, GError**); + + using arv_device_set_string_feature_value_t = void (*)(ArvDevice*, const char*, const char*, GError**); + using arv_device_set_float_feature_value_t = void (*)(ArvDevice*, const char*, double, GError**); + using arv_device_set_integer_feature_value_t = void (*)(ArvDevice*, const char*, int64_t, GError**); + + using arv_device_get_string_feature_value_t = const char* (*)(ArvDevice*, const char*, GError**); + using arv_device_get_integer_feature_value_t = int (*)(ArvDevice*, const char*, GError**); + using arv_device_get_float_feature_value_t = double (*)(ArvDevice*, const char*, GError**); + + using arv_device_get_integer_feature_bounds_t = void (*)(ArvDevice*, const char*, int64_t*, int64_t*, GError**); + using arv_device_get_float_feature_bounds_t = void (*)(ArvDevice*, const char*, double*, double*, GError**); + + using arv_device_is_feature_available_t = bool (*)(ArvDevice*, const char*, GError**); + + using arv_device_dup_register_feature_value_t = void* (*)(ArvDevice*, const char*, uint64_t*, GError**); + + using arv_device_create_stream_t = ArvStream* (*)(ArvDevice*, ArvStreamCallback*, void*, GError**); + + using arv_buffer_new_allocate_t = ArvBuffer* (*)(size_t); + using arv_stream_push_buffer_t = void (*)(ArvStream*, ArvBuffer*); + + using arv_acquisition_mode_to_string_t = const char* (*)(ArvAcquisitionMode); + using arv_device_execute_command_t = void (*)(ArvDevice*, const char*, GError**); + using arv_stream_timeout_pop_buffer_t = ArvBuffer* (*)(ArvStream*, uint64_t); + using arv_stream_get_n_buffers_t = void (*)(ArvStream*, int32_t*, int32_t*); + using arv_buffer_get_status_t = ArvBufferStatus (*)(ArvBuffer*); + using arv_buffer_get_payload_type_t = ArvBufferPayloadType (*)(ArvBuffer*); + using arv_buffer_get_data_t = void* (*)(ArvBuffer*, size_t*); + using arv_buffer_get_part_data_t = void* (*)(ArvBuffer*, uint_fast32_t, size_t*); + using arv_buffer_get_timestamp_t = uint64_t (*)(ArvBuffer*); + using arv_device_get_feature_t = ArvGcNode* (*)(ArvDevice*, const char*); + + using arv_buffer_has_gendc_t = bool* (*)(ArvBuffer*); + using arv_buffer_get_gendc_descriptor_t = void* (*)(ArvBuffer*, size_t*); + + using arv_shutdown_t = void (*)(void); + + using arv_camera_new_t = ArvCamera* (*)(const char*, GError**); + using arv_camera_get_device_t = ArvDevice* (*)(ArvCamera*); + using arv_fake_device_new_t = ArvDevice* (*)(const char*, GError**); + using arv_set_fake_camera_genicam_filename_t = void (*)(const char*); + using arv_enable_interface_t = void (*)(const char*); + using arv_camera_create_stream_t = ArvStream* (*)(ArvCamera*, ArvStreamCallback*, void*, GError**); + using arv_fake_device_get_fake_camera_t = ArvCamera* (*)(ArvFakeDevice*); + + using arv_uv_device_set_usb_mode_t = void (*)(ArvDevice*, ArvUvUsbMode); + + struct DeviceInfo { + const char* dev_id_; + ArvDevice* device_; + ArvCamera* camera_; + + int32_t u3v_payload_size_; + int32_t image_payload_size_; + uint32_t frame_count_; + + float gain_ = -1; + float exposure_ = -1; + + int32_t int_gain_ = -1; + int32_t int_exposure_ = -1; + + float exposure_range_[2]; + + ArvStream* stream_; + + // genDC + int64_t data_offset_; + std::tuple available_comp_part; + int32_t framecount_offset_; + bool is_data_image_; - rawHeader header_info_; - }; - - public: - ~U3V(){ - if (!disposed_){ - log::debug("Trying to call dispose from distructor since disposed_ is {}", disposed_); - dispose(); - } - } + rawHeader header_info_; + }; - void dispose(){ - log::debug("U3V::dispose() :: is called"); - for (auto i=0; i(d.stream_)); - auto end = std::chrono::system_clock::now(); - log::debug("U3V::dispose() :: g_object_unref took {} ms", std::chrono::duration_cast(end-start).count()); - - start = std::chrono::system_clock::now(); - g_object_unref(reinterpret_cast(d.device_)); - end = std::chrono::system_clock::now(); - log::debug("U3V::dispose() :: g_object_unref took {} ms", std::chrono::duration_cast(end-start).count()); - } + public: + ~U3V() + { + if (!disposed_) { + log::debug("Trying to call dispose from distructor since disposed_ is {}", disposed_); + dispose(); + } + } - devices_.clear(); + void dispose() + { + log::debug("U3V::dispose() :: is called"); + for (auto i = 0; i < devices_.size(); ++i) { + auto d = devices_[i]; + arv_device_execute_command(d.device_, "AcquisitionStop", &err_); + log::debug("U3V::dispose() :: AcquisitionStop"); + /* + Note: + unref stream also unref the buffers pushed to stream + all buffers are in stream so do not undef buffres separately + */ + auto start = std::chrono::system_clock::now(); + g_object_unref(reinterpret_cast(d.stream_)); + auto end = std::chrono::system_clock::now(); + log::debug("U3V::dispose() :: g_object_unref took {} ms", std::chrono::duration_cast(end - start).count()); + + start = std::chrono::system_clock::now(); + g_object_unref(reinterpret_cast(d.device_)); + end = std::chrono::system_clock::now(); + log::debug("U3V::dispose() :: g_object_unref took {} ms", std::chrono::duration_cast(end - start).count()); + } - // arv_shutdown(); - disposed_ = true; + devices_.clear(); - log::debug("U3V::dispose() :: Instance is deleted"); - } + // arv_shutdown(); + disposed_ = true; - static void release_instance(const char * id) { - log::debug("U3V::release_instance() :: is called"); - if (instances_.count(id) == 0) { - return; - } + log::debug("U3V::dispose() :: Instance is deleted"); + } - U3V & u3v = *instances_[id].get(); - u3v.dispose(); - instances_.erase(id); - log::debug("U3V::release_instance() :: is finished"); + static void release_instance(const char* id) + { + log::debug("U3V::release_instance() :: is called"); + if (instances_.count(id) == 0) { + return; + } - } + U3V& u3v = *instances_[id].get(); + u3v.dispose(); + instances_.erase(id); + log::debug("U3V::release_instance() :: is finished"); + } - void set_gain(int32_t sensor_idx, const std::string key, double v) { + void set_gain(int32_t sensor_idx, const std::string key, double v) + { - if (is_param_integer_){ - set_gain(sensor_idx, key, static_cast(v)); - return; - } + if (is_param_integer_) { + set_gain(sensor_idx, key, static_cast(v)); + return; + } - if (sensor_idx < num_sensor_ ){ - if(devices_[sensor_idx].gain_ != v){ - err_ = set(devices_[sensor_idx].device_, key.c_str(), v); - devices_[sensor_idx].gain_ = v; + if (sensor_idx < num_sensor_) { + if (devices_[sensor_idx].gain_ != v) { + err_ = set(devices_[sensor_idx].device_, key.c_str(), v); + devices_[sensor_idx].gain_ = v; + } + return; + } else { + throw std::runtime_error("the index number " + std::to_string(sensor_idx) + " exceeds the number of sensor " + std::to_string(num_sensor_)); + } } - return; - }else{ - throw std::runtime_error("the index number " + std::to_string(sensor_idx) + " exceeds the number of sensor " + std::to_string(num_sensor_)); - } - } - void set_gain(int32_t sensor_idx, const std::string key, int32_t v) { - if (sensor_idx < num_sensor_ ){ - if(devices_[sensor_idx].int_gain_ != v){ - err_ = set(devices_[sensor_idx].device_, key.c_str(), static_cast(v)); - devices_[sensor_idx].int_gain_ = v; + void set_gain(int32_t sensor_idx, const std::string key, int32_t v) + { + if (sensor_idx < num_sensor_) { + if (devices_[sensor_idx].int_gain_ != v) { + err_ = set(devices_[sensor_idx].device_, key.c_str(), static_cast(v)); + devices_[sensor_idx].int_gain_ = v; + } + return; + } else { + throw std::runtime_error("the index number " + std::to_string(sensor_idx) + " exceeds the number of sensor " + std::to_string(num_sensor_)); + } } - return; - }else{ - throw std::runtime_error("the index number " + std::to_string(sensor_idx) + " exceeds the number of sensor " + std::to_string(num_sensor_)); - } - } - void set_exposure(int32_t sensor_idx, const std::string key, double v) { + void set_exposure(int32_t sensor_idx, const std::string key, double v) + { - if (is_param_integer_){ - set_exposure(sensor_idx, key, static_cast(v)); - return; - } + if (is_param_integer_) { + set_exposure(sensor_idx, key, static_cast(v)); + return; + } - if (sensor_idx < num_sensor_ ){ - if(devices_[sensor_idx].exposure_ != v){ - err_ = set(devices_[sensor_idx].device_, key.c_str(), v); - devices_[sensor_idx].exposure_ = v; + if (sensor_idx < num_sensor_) { + if (devices_[sensor_idx].exposure_ != v) { + err_ = set(devices_[sensor_idx].device_, key.c_str(), v); + devices_[sensor_idx].exposure_ = v; + } + return; + } else { + throw std::runtime_error("the index number " + std::to_string(sensor_idx) + " exceeds the number of sensor " + std::to_string(num_sensor_)); + } } - return; - }else{ - throw std::runtime_error("the index number " + std::to_string(sensor_idx) + " exceeds the number of sensor " + std::to_string(num_sensor_)); - } - } - void set_exposure(int32_t sensor_idx, const std::string key, int32_t v) { - if (sensor_idx < num_sensor_ ){ - if(devices_[sensor_idx].int_exposure_ != v){ - err_ = set(devices_[sensor_idx].device_, key.c_str(), static_cast(v)); - devices_[sensor_idx].int_exposure_ = v; + void set_exposure(int32_t sensor_idx, const std::string key, int32_t v) + { + if (sensor_idx < num_sensor_) { + if (devices_[sensor_idx].int_exposure_ != v) { + err_ = set(devices_[sensor_idx].device_, key.c_str(), static_cast(v)); + devices_[sensor_idx].int_exposure_ = v; + } + return; + } else { + throw std::runtime_error("the index number " + std::to_string(sensor_idx) + " exceeds the number of sensor " + std::to_string(num_sensor_)); + } } - return; - }else{ - throw std::runtime_error("the index number " + std::to_string(sensor_idx) + " exceeds the number of sensor " + std::to_string(num_sensor_)); - } - } + virtual void get(std::vector>& outs) {}; + virtual void get(std::vector& outs) {}; - virtual void get(std::vector>& outs){}; - virtual void get(std::vector& outs){}; - - void get_frame_count(std::vector& outs){ - if (num_sensor_ != devices_.size()){ - ::memcpy(outs[0], &frame_cnt_, sizeof(uint32_t)); - }else{ - for (int nd = 0; nd < num_sensor_; nd++){ - ::memcpy(outs[nd], &devices_[nd].frame_count_, sizeof(uint32_t)); + void get_frame_count(std::vector& outs) + { + if (num_sensor_ != devices_.size()) { + ::memcpy(outs[0], &frame_cnt_, sizeof(uint32_t)); + } else { + for (int nd = 0; nd < num_sensor_; nd++) { + ::memcpy(outs[nd], &devices_[nd].frame_count_, sizeof(uint32_t)); + } + } } - } - } - void get_device_info(std::vector& outs){ - if (sim_mode_||operation_mode_ == OperationMode::Came2USB2 || operation_mode_ == OperationMode::Came1USB1){ - for (int i = 0; i < num_sensor_; ++i){ - ::memcpy(outs[i], &(devices_[i].header_info_), sizeof(ion::bb::image_io::rawHeader)); - log::trace("Obtained Device info USB{}", i); + void get_device_info(std::vector& outs) + { + if (sim_mode_ || operation_mode_ == OperationMode::Came2USB2 || operation_mode_ == OperationMode::Came1USB1) { + for (int i = 0; i < num_sensor_; ++i) { + ::memcpy(outs[i], &(devices_[i].header_info_), sizeof(ion::bb::image_io::rawHeader)); + log::trace("Obtained Device info USB{}", i); + } + } else if (operation_mode_ == OperationMode::Came1USB2) { + ::memcpy(outs[0], &(devices_[device_idx_].header_info_), sizeof(ion::bb::image_io::rawHeader)); + log::trace("Obtained Device info (OperationMode::Came1USB2)"); + } } - } else if (operation_mode_ == OperationMode::Came1USB2) { - ::memcpy(outs[0], &(devices_[device_idx_].header_info_), sizeof(ion::bb::image_io::rawHeader)); - log::trace("Obtained Device info (OperationMode::Came1USB2)"); - } - } - -protected: - U3V(int32_t num_sensor, bool frame_sync, bool realtime_display_mode, bool sim_mode, int32_t width, int32_t height , float_t fps, const std::string & pixel_format, char* dev_id = nullptr) - : gobject_(GOBJECT_FILE, true), aravis_(ARAVIS_FILE, true, true), - num_sensor_(num_sensor), frame_count_method_(FrameCountMethod::UNAVAILABLE), - frame_sync_(frame_sync), realtime_display_mode_(realtime_display_mode), is_gendc_(false), is_param_integer_(false), - devices_(num_sensor), buffers_(num_sensor), operation_mode_(OperationMode::Came1USB1), frame_cnt_(0), device_idx_(-1), disposed_(false), sim_mode_(sim_mode), order_filp_(false) - { - init_symbols(); - log::debug("U3V:: 24-09-03 : Tested on device 1.2"); - log::info("Using aravis-{}.{}.{}", arv_get_major_version(), arv_get_minor_version(), arv_get_micro_version()); - } - - - void init_symbols_gobject() { - if (!gobject_.is_available()) { - throw ::std::runtime_error("libgobject-2.0 is unavailable on your system."); - } - #define GET_SYMBOL(LOCAL_VAR, TARGET_SYMBOL) \ - LOCAL_VAR = gobject_.get_symbol(TARGET_SYMBOL); \ - if (LOCAL_VAR == nullptr) { \ - throw ::std::runtime_error( \ - TARGET_SYMBOL " is unavailable on gobject-2.0"); \ + protected: + U3V(int32_t num_sensor, bool frame_sync, bool realtime_display_mode, bool sim_mode, int32_t width, int32_t height, float_t fps, const std::string& pixel_format, char* dev_id = nullptr) + : gobject_(GOBJECT_FILE, true) + , aravis_(ARAVIS_FILE, true, true) + , num_sensor_(num_sensor) + , frame_count_method_(FrameCountMethod::UNAVAILABLE) + , frame_sync_(frame_sync) + , realtime_display_mode_(realtime_display_mode) + , is_gendc_(false) + , is_param_integer_(false) + , devices_(num_sensor) + , buffers_(num_sensor) + , operation_mode_(OperationMode::Came1USB1) + , frame_cnt_(0) + , device_idx_(-1) + , disposed_(false) + , sim_mode_(sim_mode) + , order_filp_(false) + { + init_symbols(); + log::debug("U3V:: 24-09-03 : Tested on device 1.2"); + log::info("Using aravis-{}.{}.{}", arv_get_major_version(), arv_get_minor_version(), arv_get_micro_version()); } - GET_SYMBOL(g_object_unref, "g_object_unref"); + void init_symbols_gobject() + { + if (!gobject_.is_available()) { + throw ::std::runtime_error("libgobject-2.0 is unavailable on your system."); + } - #undef GET_SYMBOL +#define GET_SYMBOL(LOCAL_VAR, TARGET_SYMBOL) \ + LOCAL_VAR = gobject_.get_symbol(TARGET_SYMBOL); \ + if (LOCAL_VAR == nullptr) { \ + throw ::std::runtime_error( \ + TARGET_SYMBOL " is unavailable on gobject-2.0"); \ } - void init_symbols_aravis() { + GET_SYMBOL(g_object_unref, "g_object_unref"); - #define GET_SYMBOL(LOCAL_VAR, TARGET_SYMBOL) \ - LOCAL_VAR = aravis_.get_symbol(TARGET_SYMBOL); \ - if (LOCAL_VAR == nullptr) { \ - throw ::std::runtime_error( \ - TARGET_SYMBOL " is unavailable on aravis-0.8"); \ +#undef GET_SYMBOL } - GET_SYMBOL(arv_get_major_version, "arv_get_major_version"); - GET_SYMBOL(arv_get_minor_version, "arv_get_minor_version"); - GET_SYMBOL(arv_get_micro_version, "arv_get_micro_version"); - - GET_SYMBOL(arv_get_device_protocol, "arv_get_device_protocol"); - - GET_SYMBOL(arv_update_device_list, "arv_update_device_list"); - GET_SYMBOL(arv_get_n_devices, "arv_get_n_devices"); - - GET_SYMBOL(arv_get_device_id, "arv_get_device_id"); - GET_SYMBOL(arv_get_device_model, "arv_get_device_model"); - GET_SYMBOL(arv_get_device_serial_nbr, "arv_get_device_serial_nbr"); - - GET_SYMBOL(arv_open_device, "arv_open_device"); - GET_SYMBOL(arv_device_set_string_feature_value, "arv_device_set_string_feature_value"); - GET_SYMBOL(arv_device_set_float_feature_value, "arv_device_set_float_feature_value"); - GET_SYMBOL(arv_device_set_integer_feature_value, "arv_device_set_integer_feature_value"); - GET_SYMBOL(arv_device_get_string_feature_value, "arv_device_get_string_feature_value"); - GET_SYMBOL(arv_device_get_integer_feature_value, "arv_device_get_integer_feature_value"); - GET_SYMBOL(arv_device_get_float_feature_value, "arv_device_get_float_feature_value"); - GET_SYMBOL(arv_device_get_integer_feature_bounds, "arv_device_get_integer_feature_bounds"); - GET_SYMBOL(arv_device_get_float_feature_bounds, "arv_device_get_float_feature_bounds"); - - GET_SYMBOL(arv_device_dup_register_feature_value, "arv_device_dup_register_feature_value"); - - GET_SYMBOL(arv_device_create_stream, "arv_device_create_stream"); - GET_SYMBOL(arv_buffer_new_allocate, "arv_buffer_new_allocate"); - GET_SYMBOL(arv_stream_push_buffer, "arv_stream_push_buffer"); - GET_SYMBOL(arv_stream_get_n_buffers, "arv_stream_get_n_buffers"); - GET_SYMBOL(arv_acquisition_mode_to_string, "arv_acquisition_mode_to_string"); - GET_SYMBOL(arv_device_is_feature_available, "arv_device_is_feature_available"); - GET_SYMBOL(arv_device_execute_command, "arv_device_execute_command"); - GET_SYMBOL(arv_stream_timeout_pop_buffer, "arv_stream_timeout_pop_buffer"); - GET_SYMBOL(arv_buffer_get_status, "arv_buffer_get_status"); - GET_SYMBOL(arv_buffer_get_payload_type, "arv_buffer_get_payload_type"); - GET_SYMBOL(arv_buffer_get_data, "arv_buffer_get_data"); - GET_SYMBOL(arv_buffer_get_part_data, "arv_buffer_get_part_data"); - GET_SYMBOL(arv_buffer_get_timestamp, "arv_buffer_get_timestamp"); - GET_SYMBOL(arv_device_get_feature, "arv_device_get_feature"); - - GET_SYMBOL(arv_buffer_has_gendc, "arv_buffer_has_gendc"); - GET_SYMBOL(arv_buffer_get_gendc_descriptor, "arv_buffer_get_gendc_descriptor"); - - GET_SYMBOL(arv_shutdown, "arv_shutdown"); - - GET_SYMBOL(arv_camera_create_stream, "arv_camera_create_stream"); - GET_SYMBOL(arv_camera_new, "arv_camera_new"); - GET_SYMBOL(arv_camera_get_device, "arv_camera_get_device"); - GET_SYMBOL(arv_fake_device_new, "arv_fake_device_new"); - GET_SYMBOL(arv_set_fake_camera_genicam_filename, "arv_set_fake_camera_genicam_filename"); - GET_SYMBOL(arv_enable_interface, "arv_enable_interface"); - GET_SYMBOL(arv_fake_device_get_fake_camera, "arv_fake_device_get_fake_camera"); - GET_SYMBOL(arv_uv_device_set_usb_mode, "arv_uv_device_set_usb_mode"); - #undef GET_SYMBOL - } - - void init_symbols() { - init_symbols_gobject(); - init_symbols_aravis(); - } - - int32_t get_frame_count_from_genDC_descriptor(ArvBuffer * buf, DeviceInfo& d){ - int32_t frame_count = 0;; - memcpy (&frame_count, ((char *) arv_buffer_get_data(buf, nullptr) + d.framecount_offset_), sizeof(int32_t)); - return frame_count; - } - - template - GError* set(ArvDevice* dev_handle, const char* key, T v) { - return set_feature_value(dev_handle, key, v); - } - - template - GError* get(ArvDevice* dev_handle, const char* key, T* v) { - T vp; - err_ = get_feature_value(dev_handle, key, vp); - *v = vp; - return err_; - } - - GError* set_feature_value(ArvDevice *device, const char *feature, const char *value){ - arv_device_set_string_feature_value (device, feature, value, &err_); - return err_; - } - - GError* set_feature_value(ArvDevice *device, const char *feature, double value){ - double min_v, max_v; - arv_device_get_float_feature_bounds (device, feature, &min_v, &max_v, &err_); - if (err_ != nullptr) { - return err_; - } - value = (std::max)(min_v, value); - value = (std::min)(max_v, value); - - arv_device_set_float_feature_value (device, feature, value, &err_); - - return err_; - } - - GError* set_feature_value(ArvDevice *device, const char *feature, int64_t value){ - int64_t min_v, max_v; - arv_device_get_integer_feature_bounds(device, feature, &min_v, &max_v, &err_); - if (err_ != nullptr) { - return err_; - } - value = (std::max)(min_v, value); - value = (std::min)(max_v, value); - - arv_device_set_integer_feature_value (device, feature, value, &err_); - return err_; - } - - GError* get_feature_value(ArvDevice *device, const char *feature, int64_t& value){ - value = arv_device_get_integer_feature_value(device, feature, &err_); - return err_; - } - - void validate_user_input(int32_t num_detected_device, char* dev_id){ - if (num_detected_device < num_sensor_){ - log::info("{} device is found; but the num_sensor is set to {}", num_detected_device, num_sensor_); - throw std::runtime_error("Device number is not match, please set num_device again"); - } - frame_sync_ = num_sensor_ > 1 ? frame_sync_ : false; - - if (num_detected_device > num_sensor_ && dev_id == nullptr) { - log::info("{} devices are found; The first {} device is selected", num_detected_device, num_sensor_); - } + void init_symbols_aravis() + { - log::info("Creating U3V instance with {} sensors...", num_sensor_); - log::info("Acquisition option::{} is {}", "frame_sync_", frame_sync_); - log::info("Acquisition option::{} is {}", "realtime_display_mode_", realtime_display_mode_); +#define GET_SYMBOL(LOCAL_VAR, TARGET_SYMBOL) \ + LOCAL_VAR = aravis_.get_symbol(TARGET_SYMBOL); \ + if (LOCAL_VAR == nullptr) { \ + throw ::std::runtime_error( \ + TARGET_SYMBOL " is unavailable on aravis-0.8"); \ } - void command_acquisition_mode_contd_and_start(){ - for (auto i=0; imessage); + GET_SYMBOL(arv_get_major_version, "arv_get_major_version"); + GET_SYMBOL(arv_get_minor_version, "arv_get_minor_version"); + GET_SYMBOL(arv_get_micro_version, "arv_get_micro_version"); + + GET_SYMBOL(arv_get_device_protocol, "arv_get_device_protocol"); + + GET_SYMBOL(arv_update_device_list, "arv_update_device_list"); + GET_SYMBOL(arv_get_n_devices, "arv_get_n_devices"); + + GET_SYMBOL(arv_get_device_id, "arv_get_device_id"); + GET_SYMBOL(arv_get_device_model, "arv_get_device_model"); + GET_SYMBOL(arv_get_device_serial_nbr, "arv_get_device_serial_nbr"); + + GET_SYMBOL(arv_open_device, "arv_open_device"); + GET_SYMBOL(arv_device_set_string_feature_value, "arv_device_set_string_feature_value"); + GET_SYMBOL(arv_device_set_float_feature_value, "arv_device_set_float_feature_value"); + GET_SYMBOL(arv_device_set_integer_feature_value, "arv_device_set_integer_feature_value"); + GET_SYMBOL(arv_device_get_string_feature_value, "arv_device_get_string_feature_value"); + GET_SYMBOL(arv_device_get_integer_feature_value, "arv_device_get_integer_feature_value"); + GET_SYMBOL(arv_device_get_float_feature_value, "arv_device_get_float_feature_value"); + GET_SYMBOL(arv_device_get_integer_feature_bounds, "arv_device_get_integer_feature_bounds"); + GET_SYMBOL(arv_device_get_float_feature_bounds, "arv_device_get_float_feature_bounds"); + + GET_SYMBOL(arv_device_dup_register_feature_value, "arv_device_dup_register_feature_value"); + + GET_SYMBOL(arv_device_create_stream, "arv_device_create_stream"); + GET_SYMBOL(arv_buffer_new_allocate, "arv_buffer_new_allocate"); + GET_SYMBOL(arv_stream_push_buffer, "arv_stream_push_buffer"); + GET_SYMBOL(arv_stream_get_n_buffers, "arv_stream_get_n_buffers"); + GET_SYMBOL(arv_acquisition_mode_to_string, "arv_acquisition_mode_to_string"); + GET_SYMBOL(arv_device_is_feature_available, "arv_device_is_feature_available"); + GET_SYMBOL(arv_device_execute_command, "arv_device_execute_command"); + GET_SYMBOL(arv_stream_timeout_pop_buffer, "arv_stream_timeout_pop_buffer"); + GET_SYMBOL(arv_buffer_get_status, "arv_buffer_get_status"); + GET_SYMBOL(arv_buffer_get_payload_type, "arv_buffer_get_payload_type"); + GET_SYMBOL(arv_buffer_get_data, "arv_buffer_get_data"); + GET_SYMBOL(arv_buffer_get_part_data, "arv_buffer_get_part_data"); + GET_SYMBOL(arv_buffer_get_timestamp, "arv_buffer_get_timestamp"); + GET_SYMBOL(arv_device_get_feature, "arv_device_get_feature"); + + GET_SYMBOL(arv_buffer_has_gendc, "arv_buffer_has_gendc"); + GET_SYMBOL(arv_buffer_get_gendc_descriptor, "arv_buffer_get_gendc_descriptor"); + + GET_SYMBOL(arv_shutdown, "arv_shutdown"); + + GET_SYMBOL(arv_camera_create_stream, "arv_camera_create_stream"); + GET_SYMBOL(arv_camera_new, "arv_camera_new"); + GET_SYMBOL(arv_camera_get_device, "arv_camera_get_device"); + GET_SYMBOL(arv_fake_device_new, "arv_fake_device_new"); + GET_SYMBOL(arv_set_fake_camera_genicam_filename, "arv_set_fake_camera_genicam_filename"); + GET_SYMBOL(arv_enable_interface, "arv_enable_interface"); + GET_SYMBOL(arv_fake_device_get_fake_camera, "arv_fake_device_get_fake_camera"); + GET_SYMBOL(arv_uv_device_set_usb_mode, "arv_uv_device_set_usb_mode"); +#undef GET_SYMBOL } - log::info("\tDevice/USB {}::{} : {}", i, "Command", "AcquisitionMode"); - arv_device_execute_command(devices_[i].device_, "AcquisitionStart", &err_); - if (err_) { - throw std::runtime_error(err_->message); + void init_symbols() + { + init_symbols_gobject(); + init_symbols_aravis(); } - log::info("\tDevice/USB {}::{} : {}", i, "Command", "AcquisitionStart"); - } - } - - void open_fake_devices(int32_t width, int32_t height , float_t fps, const std::string & pixel_format){ - auto path = std::getenv("GENICAM_FILENAME"); - if (path == nullptr){ - throw std::runtime_error("Please define GENICAM_FILENAME by `set GENICAM_FILENAME=` or `export GENICAM_FILENAME=`"); - } - pixel_format_ = pixel_format; - arv_set_fake_camera_genicam_filename (path); - - arv_enable_interface ("Fake"); - log::info("Creating U3V instance with {} fake sensors...", num_sensor_); - auto fake_camera0 = arv_camera_new ("Fake_1", &err_); - if (err_) { - throw std::runtime_error(err_->message); - } - auto fake_device0 = arv_camera_get_device(fake_camera0); - devices_[0].device_ = fake_device0; - devices_[0].dev_id_= "fake_0"; - devices_[0].camera_ = fake_camera0; - if (num_sensor_==2){ - // aravis only provide on ARV_FAKE_DEVICE_ID https://github.com/Sensing-Dev/aravis/blob/main/src/arvfakeinterface.c - auto fake_camera1 = arv_camera_new ("Fake_1", &err_); - if (err_) { - throw std::runtime_error(err_->message); - } - auto fake_device1 = arv_camera_get_device(fake_camera1); - devices_[1].device_ = fake_device1; - devices_[1].dev_id_= "fake_1"; - devices_[1].camera_ = fake_camera1; - } - // Config fake cameras - for (int i = 0;i< num_sensor_;i++){ - // setting the params if it is not zero - log::info("Width {}, Height {} PixelFormat {}...", width, height, pixel_format_); - arv_device_set_integer_feature_value (devices_[i].device_, "Width", width, &err_); - if (err_) { - throw std::runtime_error(err_->message); - } - arv_device_set_integer_feature_value (devices_[i].device_, "Height", height, &err_); - if (err_) { - throw std::runtime_error(err_->message); - } - arv_device_set_float_feature_value (devices_[i].device_, "AcquisitionFrameRate",fps, &err_); - if (err_) { - throw std::runtime_error(err_->message); - } - if (pixel_format_ != "Mono8"){ - arv_device_set_string_feature_value(devices_[i].device_, "PixelFormat", pixel_format.c_str(), &err_); - if (err_) { - throw std::runtime_error(err_->message); - } + int32_t get_frame_count_from_genDC_descriptor(ArvBuffer* buf, DeviceInfo& d) + { + int32_t frame_count = 0; + ; + memcpy(&frame_count, ((char*)arv_buffer_get_data(buf, nullptr) + d.framecount_offset_), sizeof(int32_t)); + return frame_count; } - devices_[i].u3v_payload_size_ = arv_device_get_integer_feature_value (devices_[i].device_, "PayloadSize", &err_); - if (err_) { - throw std::runtime_error(err_->message); - } - auto px =arv_device_get_integer_feature_value(devices_[i].device_, "PixelFormat", &err_); - if (err_) { - throw std::runtime_error(err_->message); - } - auto fps = arv_device_get_float_feature_value(devices_[i].device_, "AcquisitionFrameRate", &err_); - if (err_) { - throw std::runtime_error(err_->message); + + template + GError* set(ArvDevice* dev_handle, const char* key, T v) + { + return set_feature_value(dev_handle, key, v); } - struct rawHeader header= { 1, width, height, - 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, - width, height, width, height, static_cast(fps), px, 0}; - devices_[i].header_info_ = header; - devices_[i].image_payload_size_ = devices_[i].u3v_payload_size_; - devices_[i].frame_count_ = 0; - } - } + template + GError* get(ArvDevice* dev_handle, const char* key, T* v) + { + T vp; + err_ = get_feature_value(dev_handle, key, vp); + *v = vp; + return err_; + } - void open_real_devices(int32_t num_detected_device, int32_t num_usb_to_open, char* dev_id){ + GError* set_feature_value(ArvDevice* device, const char* feature, const char* value) + { + arv_device_set_string_feature_value(device, feature, value, &err_); + return err_; + } - int index_on_detected_device = 0; - int index_on_opened_device = 0; + GError* set_feature_value(ArvDevice* device, const char* feature, double value) + { + double min_v, max_v; + arv_device_get_float_feature_bounds(device, feature, &min_v, &max_v, &err_); + if (err_ != nullptr) { + return err_; + } + value = (std::max)(min_v, value); + value = (std::min)(max_v, value); - while (index_on_detected_device < num_detected_device && index_on_opened_device < num_usb_to_open){ + arv_device_set_float_feature_value(device, feature, value, &err_); - const char* device_protocol = arv_get_device_protocol(index_on_detected_device); - if (strcmp(device_protocol, "USB3Vision") == 0){ + return err_; + } - if (dev_id != nullptr && dev_id == arv_get_device_id (index_on_detected_device)){ - /* if device id is specified - TODO: dev_id may be more than 1 - */ - devices_[index_on_opened_device].dev_id_ = dev_id; - } - else{ - /* if device id is not specified */ - devices_[index_on_opened_device].dev_id_ = arv_get_device_id (index_on_detected_device); + GError* set_feature_value(ArvDevice* device, const char* feature, int64_t value) + { + int64_t min_v, max_v; + arv_device_get_integer_feature_bounds(device, feature, &min_v, &max_v, &err_); + if (err_ != nullptr) { + return err_; } + value = (std::max)(min_v, value); + value = (std::min)(max_v, value); - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "DeviceID", devices_[index_on_opened_device].dev_id_); + arv_device_set_integer_feature_value(device, feature, value, &err_); + return err_; + } - devices_[index_on_opened_device].device_ = arv_open_device(devices_[index_on_opened_device].dev_id_, &err_); - if (err_ ) { - throw std::runtime_error(err_->message); - } + GError* get_feature_value(ArvDevice* device, const char* feature, int64_t& value) + { + value = arv_device_get_integer_feature_value(device, feature, &err_); + return err_; + } - if (devices_[index_on_opened_device].device_ == nullptr) { - throw std::runtime_error("device is null"); + void validate_user_input(int32_t num_detected_device, char* dev_id) + { + if (num_detected_device < num_sensor_) { + log::info("{} device is found; but the num_sensor is set to {}", num_detected_device, num_sensor_); + throw std::runtime_error("Device number is not match, please set num_device again"); } + frame_sync_ = num_sensor_ > 1 ? frame_sync_ : false; - pixel_format_ = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "PixelFormat", &err_); - if (err_ ) { - log::error(err_->message); - err_ = nullptr; - }else{ - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "PixelFormat", pixel_format_); - } - - // Here PayloadSize is the one for U3V data - devices_[index_on_opened_device].u3v_payload_size_ = arv_device_get_integer_feature_value(devices_[index_on_opened_device].device_, "PayloadSize", &err_); - if (err_ ) { - throw std::runtime_error(err_->message); + if (num_detected_device > num_sensor_ && dev_id == nullptr) { + log::info("{} devices are found; The first {} device is selected", num_detected_device, num_sensor_); } - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "PayloadSize", devices_[index_on_opened_device].u3v_payload_size_); - // check it the device has gendc mode ============================== - is_gendc_ = arv_device_is_feature_available(devices_[index_on_opened_device].device_, "GenDCDescriptor", &err_); - if (err_) { - throw std::runtime_error(err_->message); - } - is_gendc_ &= arv_device_is_feature_available(devices_[index_on_opened_device].device_, "GenDCStreamingMode", &err_); - if (err_) { - throw std::runtime_error(err_->message); - } + log::info("Creating U3V instance with {} sensors...", num_sensor_); + log::info("Acquisition option::{} is {}", "frame_sync_", frame_sync_); + log::info("Acquisition option::{} is {}", "realtime_display_mode_", realtime_display_mode_); + } - // check it the device is gendc mode =============================== - if (is_gendc_){ - const char * streaming_mode; - streaming_mode = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "GenDCStreamingMode", &err_); + void command_acquisition_mode_contd_and_start() + { + for (auto i = 0; i < devices_.size(); ++i) { + arv_device_set_string_feature_value(devices_[i].device_, "AcquisitionMode", arv_acquisition_mode_to_string(ARV_ACQUISITION_MODE_CONTINUOUS), &err_); if (err_) { throw std::runtime_error(err_->message); } - is_gendc_ &= (strcmp(streaming_mode, "On")==0); - } - - // Some type of U3V Camera supports Frame count generated by its device - const char* device_vender_name; - device_vender_name = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "DeviceVendorName", &err_); - if (err_) { - log::error(err_->message); - err_ = nullptr; - }else{ - if (strcmp(device_vender_name, "Sony Semiconductor Solutions Corporation")==0){ - const char* device_model_name; - device_model_name = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "DeviceModelName", &err_); - if (err_) { - log::error(err_->message); - err_ = nullptr; - }else{ - if (strcmp(device_model_name, " ")==0){ - is_param_integer_ = true; - frame_count_method_ = FrameCountMethod::TIMESTAMP; - arv_uv_device_set_usb_mode(devices_[index_on_opened_device].device_, ARV_UV_USB_MODE_SYNC); //hotfix for v1.0 - } - } - if (is_gendc_){ - frame_count_method_ = FrameCountMethod::TYPESPECIFIC3; - order_filp_ = true; - } - } - } - - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "frame_count method is ", - frame_count_method_ == FrameCountMethod::TIMESTAMP ? "Timestamp": - frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 ? "TypeSpecific" : "Unavailabe"); + log::info("\tDevice/USB {}::{} : {}", i, "Command", "AcquisitionMode"); - // Check each parameters for GenDC device ========================== - int group_id = 0; - if (is_gendc_){ - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "GenDC", "Available"); - uint64_t gendc_desc_size = 0; - char* buffer = reinterpret_cast(arv_device_dup_register_feature_value(devices_[index_on_opened_device].device_,"GenDCDescriptor", &gendc_desc_size, &err_ )); + arv_device_execute_command(devices_[i].device_, "AcquisitionStart", &err_); if (err_) { throw std::runtime_error(err_->message); } + log::info("\tDevice/USB {}::{} : {}", i, "Command", "AcquisitionStart"); + } + } - if(isGenDC(buffer)){ - gendc_descriptor_= ContainerHeader(buffer); - std::tuple data_comp_and_part = gendc_descriptor_.getFirstAvailableDataOffset(true); - if (std::get<0>(data_comp_and_part) == -1){ - devices_[index_on_opened_device].is_data_image_ = false; - data_comp_and_part = gendc_descriptor_.getFirstAvailableDataOffset(false); - if (std::get<0>(data_comp_and_part) == -1){ - throw std::runtime_error("None of the data in GenDC is available\n"); - } - }else{ - devices_[index_on_opened_device].is_data_image_ = true; - } - devices_[index_on_opened_device].data_offset_ = gendc_descriptor_.getDataOffset(std::get<0>(data_comp_and_part), std::get<1>(data_comp_and_part)); - devices_[index_on_opened_device].image_payload_size_ = gendc_descriptor_.getDataSize(std::get<0>(data_comp_and_part), std::get<1>(data_comp_and_part)); - if (frame_count_method_ == FrameCountMethod::TYPESPECIFIC3){ - devices_[index_on_opened_device].framecount_offset_ = gendc_descriptor_.getOffsetFromTypeSpecific(std::get<0>(data_comp_and_part), std::get<1>(data_comp_and_part), 3, 0); - } - int32_t image_component_index = gendc_descriptor_.getFirstComponentIndexByTypeID(ComponentIDIntensity); - if (image_component_index == -1){ - throw ::std::runtime_error("No available component found"); - } - ComponentHeader image_component = gendc_descriptor_.getComponentByIndex(image_component_index); - group_id = gendc_descriptor_.getComponentByIndex(image_component_index).getGroupID(); - } - free(buffer); - }else{ - devices_[index_on_opened_device].data_offset_ = 0; - devices_[index_on_opened_device].image_payload_size_ = devices_[index_on_opened_device].u3v_payload_size_; - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "GenDC", "Not Supported"); + void open_fake_devices(int32_t width, int32_t height, float_t fps, const std::string& pixel_format) + { + auto path = std::getenv("GENICAM_FILENAME"); + if (path == nullptr) { + throw std::runtime_error("Please define GENICAM_FILENAME by `set GENICAM_FILENAME=` or `export GENICAM_FILENAME=`"); } + pixel_format_ = pixel_format; + arv_set_fake_camera_genicam_filename(path); + arv_enable_interface("Fake"); + log::info("Creating U3V instance with {} fake sensors...", num_sensor_); - // Set Device Info ================================================= - { - int32_t wi = arv_device_get_integer_feature_value(devices_[index_on_opened_device].device_, "Width", &err_); + auto fake_camera0 = arv_camera_new("Fake_1", &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + auto fake_device0 = arv_camera_get_device(fake_camera0); + devices_[0].device_ = fake_device0; + devices_[0].dev_id_ = "fake_0"; + devices_[0].camera_ = fake_camera0; + if (num_sensor_ == 2) { + // aravis only provide on ARV_FAKE_DEVICE_ID https://github.com/Sensing-Dev/aravis/blob/main/src/arvfakeinterface.c + auto fake_camera1 = arv_camera_new("Fake_1", &err_); if (err_) { throw std::runtime_error(err_->message); } - int32_t hi = arv_device_get_integer_feature_value(devices_[index_on_opened_device].device_, "Height", &err_); + auto fake_device1 = arv_camera_get_device(fake_camera1); + devices_[1].device_ = fake_device1; + devices_[1].dev_id_ = "fake_1"; + devices_[1].camera_ = fake_camera1; + } + // Config fake cameras + for (int i = 0; i < num_sensor_; i++) { + // setting the params if it is not zero + log::info("Width {}, Height {} PixelFormat {}...", width, height, pixel_format_); + arv_device_set_integer_feature_value(devices_[i].device_, "Width", width, &err_); if (err_) { throw std::runtime_error(err_->message); } - double fps = 0.0; - if (arv_device_is_feature_available(devices_[index_on_opened_device].device_, "AcquisitionFrameRate", &err_)){ - fps = arv_device_get_float_feature_value(devices_[index_on_opened_device].device_, "AcquisitionFrameRate", &err_); + arv_device_set_integer_feature_value(devices_[i].device_, "Height", height, &err_); + if (err_) { + throw std::runtime_error(err_->message); } + arv_device_set_float_feature_value(devices_[i].device_, "AcquisitionFrameRate", fps, &err_); if (err_) { throw std::runtime_error(err_->message); } - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "Width", wi); - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "Height", hi); - - int32_t px = arv_device_get_integer_feature_value (devices_[index_on_opened_device].device_, "PixelFormat", &err_); - if (err_ || px == 0){ - log::info("The pixel format is not supported for header info"); - err_ = nullptr; - px = 0; + if (pixel_format_ != "Mono8") { + arv_device_set_string_feature_value(devices_[i].device_, "PixelFormat", pixel_format.c_str(), &err_); + if (err_) { + throw std::runtime_error(err_->message); + } } - - devices_[index_on_opened_device].header_info_ = { 1, wi, hi, - 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, - wi, hi, wi, hi, static_cast(fps), px, group_id - }; - } - - if (index_on_opened_device == 0 && arv_device_is_feature_available(devices_[index_on_opened_device].device_, "OperationMode", &err_)){ + devices_[i].u3v_payload_size_ = arv_device_get_integer_feature_value(devices_[i].device_, "PayloadSize", &err_); if (err_) { throw std::runtime_error(err_->message); } - const char* operation_mode_in_string; - operation_mode_in_string = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "OperationMode", &err_); + auto px = arv_device_get_integer_feature_value(devices_[i].device_, "PixelFormat", &err_); if (err_) { throw std::runtime_error(err_->message); } - if (strcmp(operation_mode_in_string, "Came2USB1")==0){ - operation_mode_ = OperationMode::Came2USB1; - }else if (strcmp(operation_mode_in_string, "Came1USB1")==0){ - operation_mode_ = OperationMode::Came1USB1; - }else if (strcmp(operation_mode_in_string, "Came2USB2")==0){ - operation_mode_ = OperationMode::Came2USB2; - }else if (strcmp(operation_mode_in_string, "Came1USB2")==0){ - operation_mode_ = OperationMode::Came1USB2; - num_usb_to_open += 1; - devices_.resize(num_usb_to_open); - buffers_.resize(num_usb_to_open); + auto fps = arv_device_get_float_feature_value(devices_[i].device_, "AcquisitionFrameRate", &err_); + if (err_) { + throw std::runtime_error(err_->message); } - log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "OperationMode", operation_mode_in_string); + struct rawHeader header = { 1, width, height, + 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + width, height, width, height, static_cast(fps), px, 0 }; + devices_[i].header_info_ = header; + devices_[i].image_payload_size_ = devices_[i].u3v_payload_size_; + devices_[i].frame_count_ = 0; } - index_on_opened_device += 1; - }else{ - log::info("\tDevice/USB {}::{} : {} ... skipped", index_on_opened_device, "device protocol", device_protocol); } - index_on_detected_device += 1; - } - } + void open_real_devices(int32_t num_detected_device, int32_t num_usb_to_open, char* dev_id) + { - void create_stream_and_start_acquisition(bool specific_device_to_flip_order){ - /* - * ion-kit starts the acquisition before stream creation This is a tentative fix only in ion-kit due to hardware issue - * In aravis, the acquisition should be done afterward. Since this maps better with GenAPI, where buffers - * must be pushed to DataStream objectsbefore DataStream acquisition is started. - * refer to https://github.com/AravisProject/aravis/blob/2ebaa8661761ea4bbc4df878aa67b4a9e1a9a3b9/docs/reference/aravis/porting-0.10.md - */ - if (specific_device_to_flip_order){ - log::info("Execute AcquisitionStart before create stream on this device."); - command_acquisition_mode_contd_and_start(); - } - //start streaming after AcquisitionStart - for (auto i=0; imessage); - } - if (devices_[i].stream_ == nullptr) { - throw std::runtime_error("stream is null"); - } - } + int index_on_detected_device = 0; + int index_on_opened_device = 0; - if (! specific_device_to_flip_order){ - command_acquisition_mode_contd_and_start(); - } - } + while (index_on_detected_device < num_detected_device && index_on_opened_device < num_usb_to_open) { - void allocate_buffers(){ - for (auto i=0; i &bufs, int timeout_us){ - uint32_t max_cnt = 0; - while (true) { - // Update max_cnt - for (int i = 0; i < num_sensor_; ++i) { - if (max_cnt < devices_[i].frame_count_) { - max_cnt = devices_[i].frame_count_; - } - } + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "DeviceID", devices_[index_on_opened_device].dev_id_); - // Check all count is same as max_cnt; - bool synchronized = true; - for (int i = 0; i < num_sensor_; ++i) { - synchronized &= devices_[i].frame_count_ == max_cnt; - } + devices_[index_on_opened_device].device_ = arv_open_device(devices_[index_on_opened_device].dev_id_, &err_); + if (err_) { + throw std::runtime_error(err_->message); + } - // If it is synchronized, break the loop - if (synchronized) { - break; - } + if (devices_[index_on_opened_device].device_ == nullptr) { + throw std::runtime_error("device is null"); + } - // Acquire buffer until cnt is at least max_cnt - for (int i = 0; i < devices_.size(); ++i) { - while (devices_[i].frame_count_ < max_cnt) { - arv_stream_push_buffer(devices_[i].stream_, bufs[i]); - bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); + pixel_format_ = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "PixelFormat", &err_); + if (err_) { + log::error(err_->message); + err_ = nullptr; + } else { + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "PixelFormat", pixel_format_); + } - if (bufs[i] == nullptr) { - log::error("pop_buffer failed when sync frame due to timeout ({}s)", timeout_us * 1e-6f); - throw ::std::runtime_error("buffer is null"); + // Here PayloadSize is the one for U3V data + devices_[index_on_opened_device].u3v_payload_size_ = arv_device_get_integer_feature_value(devices_[index_on_opened_device].device_, "PayloadSize", &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "PayloadSize", devices_[index_on_opened_device].u3v_payload_size_); + + // check it the device has gendc mode ============================== + is_gendc_ = arv_device_is_feature_available(devices_[index_on_opened_device].device_, "GenDCDescriptor", &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + is_gendc_ &= arv_device_is_feature_available(devices_[index_on_opened_device].device_, "GenDCStreamingMode", &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + + // check it the device is gendc mode =============================== + if (is_gendc_) { + const char* streaming_mode; + streaming_mode = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "GenDCStreamingMode", &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + is_gendc_ &= (strcmp(streaming_mode, "On") == 0); + } + + // Some type of U3V Camera supports Frame count generated by its device + const char* device_vender_name; + device_vender_name = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "DeviceVendorName", &err_); + if (err_) { + log::error(err_->message); + err_ = nullptr; + } else { + if (strcmp(device_vender_name, "Sony Semiconductor Solutions Corporation") == 0) { + const char* device_model_name; + device_model_name = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "DeviceModelName", &err_); + if (err_) { + log::error(err_->message); + err_ = nullptr; + } else { + if (strcmp(device_model_name, " ") == 0) { + is_param_integer_ = true; + frame_count_method_ = FrameCountMethod::TIMESTAMP; + arv_uv_device_set_usb_mode(devices_[index_on_opened_device].device_, ARV_UV_USB_MODE_SYNC); // hotfix for v1.0 + } + } + if (is_gendc_) { + frame_count_method_ = FrameCountMethod::TYPESPECIFIC3; + order_filp_ = true; + } + } + } + + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "frame_count method is ", + frame_count_method_ == FrameCountMethod::TIMESTAMP ? "Timestamp" : frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 ? "TypeSpecific" + : "Unavailabe"); + + // Check each parameters for GenDC device ========================== + int group_id = 0; + if (is_gendc_) { + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "GenDC", "Available"); + uint64_t gendc_desc_size = 0; + char* buffer = reinterpret_cast(arv_device_dup_register_feature_value(devices_[index_on_opened_device].device_, "GenDCDescriptor", &gendc_desc_size, &err_)); + if (err_) { + throw std::runtime_error(err_->message); + } + + if (isGenDC(buffer)) { + gendc_descriptor_ = ContainerHeader(buffer); + std::tuple data_comp_and_part = gendc_descriptor_.getFirstAvailableDataOffset(true); + if (std::get<0>(data_comp_and_part) == -1) { + devices_[index_on_opened_device].is_data_image_ = false; + data_comp_and_part = gendc_descriptor_.getFirstAvailableDataOffset(false); + if (std::get<0>(data_comp_and_part) == -1) { + throw std::runtime_error("None of the data in GenDC is available\n"); + } + } else { + devices_[index_on_opened_device].is_data_image_ = true; + } + devices_[index_on_opened_device].data_offset_ = gendc_descriptor_.getDataOffset(std::get<0>(data_comp_and_part), std::get<1>(data_comp_and_part)); + devices_[index_on_opened_device].image_payload_size_ = gendc_descriptor_.getDataSize(std::get<0>(data_comp_and_part), std::get<1>(data_comp_and_part)); + if (frame_count_method_ == FrameCountMethod::TYPESPECIFIC3) { + devices_[index_on_opened_device].framecount_offset_ = gendc_descriptor_.getOffsetFromTypeSpecific(std::get<0>(data_comp_and_part), std::get<1>(data_comp_and_part), 3, 0); + } + int32_t image_component_index = gendc_descriptor_.getFirstComponentIndexByTypeID(ComponentIDIntensity); + if (image_component_index == -1) { + throw ::std::runtime_error("No available component found"); + } + ComponentHeader image_component = gendc_descriptor_.getComponentByIndex(image_component_index); + group_id = gendc_descriptor_.getComponentByIndex(image_component_index).getGroupID(); + } + free(buffer); + } else { + devices_[index_on_opened_device].data_offset_ = 0; + devices_[index_on_opened_device].image_payload_size_ = devices_[index_on_opened_device].u3v_payload_size_; + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "GenDC", "Not Supported"); + } + + // Set Device Info ================================================= + { + int32_t wi = arv_device_get_integer_feature_value(devices_[index_on_opened_device].device_, "Width", &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + int32_t hi = arv_device_get_integer_feature_value(devices_[index_on_opened_device].device_, "Height", &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + double fps = 0.0; + if (arv_device_is_feature_available(devices_[index_on_opened_device].device_, "AcquisitionFrameRate", &err_)) { + fps = arv_device_get_float_feature_value(devices_[index_on_opened_device].device_, "AcquisitionFrameRate", &err_); + } + if (err_) { + throw std::runtime_error(err_->message); + } + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "Width", wi); + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "Height", hi); + + int32_t px = arv_device_get_integer_feature_value(devices_[index_on_opened_device].device_, "PixelFormat", &err_); + if (err_ || px == 0) { + log::info("The pixel format is not supported for header info"); + err_ = nullptr; + px = 0; + } + + devices_[index_on_opened_device].header_info_ = { 1, wi, hi, + 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + wi, hi, wi, hi, static_cast(fps), px, group_id }; + } + + if (index_on_opened_device == 0 && arv_device_is_feature_available(devices_[index_on_opened_device].device_, "OperationMode", &err_)) { + if (err_) { + throw std::runtime_error(err_->message); + } + const char* operation_mode_in_string; + operation_mode_in_string = arv_device_get_string_feature_value(devices_[index_on_opened_device].device_, "OperationMode", &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + if (strcmp(operation_mode_in_string, "Came2USB1") == 0) { + operation_mode_ = OperationMode::Came2USB1; + } else if (strcmp(operation_mode_in_string, "Came1USB1") == 0) { + operation_mode_ = OperationMode::Came1USB1; + } else if (strcmp(operation_mode_in_string, "Came2USB2") == 0) { + operation_mode_ = OperationMode::Came2USB2; + } else if (strcmp(operation_mode_in_string, "Came1USB2") == 0) { + operation_mode_ = OperationMode::Came1USB2; + num_usb_to_open += 1; + devices_.resize(num_usb_to_open); + buffers_.resize(num_usb_to_open); + } + log::info("\tDevice/USB {}::{} : {}", index_on_opened_device, "OperationMode", operation_mode_in_string); + } + index_on_opened_device += 1; + } else { + log::info("\tDevice/USB {}::{} : {} ... skipped", index_on_opened_device, "device protocol", device_protocol); } - devices_[i].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 - ? static_cast(get_frame_count_from_genDC_descriptor(bufs[i], devices_[i])) - : frame_count_method_ == FrameCountMethod::TIMESTAMP - ? static_cast(arv_buffer_get_timestamp(bufs[i]) & 0x00000000FFFFFFFF) - : -1; - i == 0 ? - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[i].frame_count_, "") : - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[i].frame_count_); + + index_on_detected_device += 1; } } - } - } - void consume_old_buffer(std::vector &bufs, int timeout_us = 3 * 1000 * 1000){ - std::vector N_output_buffers(num_sensor_); - for (auto i = 0; i < num_sensor_; ++i) { - int32_t num_input_buffer; - arv_stream_get_n_buffers(devices_[i].stream_, &num_input_buffer, &(N_output_buffers[i])); - } - // if all stream has N output buffers, discard N-1 of them - for (auto i = 0; i < num_sensor_; ++i) { - for (auto j = 0; j < N_output_buffers[i] - 1; ++j) { - bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); - if (bufs[i] == nullptr) { - log::error("pop_buffer(L2) failed due to timeout ({}s)", timeout_us * 1e-6f); - throw ::std::runtime_error("buffer is null"); + void create_stream_and_start_acquisition(bool specific_device_to_flip_order) + { + /* + * ion-kit starts the acquisition before stream creation This is a tentative fix only in ion-kit due to hardware issue + * In aravis, the acquisition should be done afterward. Since this maps better with GenAPI, where buffers + * must be pushed to DataStream objectsbefore DataStream acquisition is started. + * refer to https://github.com/AravisProject/aravis/blob/2ebaa8661761ea4bbc4df878aa67b4a9e1a9a3b9/docs/reference/aravis/porting-0.10.md + */ + if (specific_device_to_flip_order) { + log::info("Execute AcquisitionStart before create stream on this device."); + command_acquisition_mode_contd_and_start(); + } + // start streaming after AcquisitionStart + for (auto i = 0; i < devices_.size(); ++i) { + devices_[i].stream_ = arv_device_create_stream(devices_[i].device_, nullptr, nullptr, &err_); + if (err_) { + throw std::runtime_error(err_->message); + } + if (devices_[i].stream_ == nullptr) { + throw std::runtime_error("stream is null"); + } } - devices_[i].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 - ? static_cast(get_frame_count_from_genDC_descriptor(bufs[i], devices_[i])) - : frame_count_method_ == FrameCountMethod::TIMESTAMP - ? static_cast(arv_buffer_get_timestamp(bufs[i]) & 0x00000000FFFFFFFF) - : -1; - i == 0 ? - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20}) [skipped for realtime display]", devices_[i].frame_count_, "") : - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20}) [skipped for realtime display]", "", devices_[i].frame_count_); - arv_stream_push_buffer(devices_[i].stream_, bufs[i]); + if (!specific_device_to_flip_order) { + command_acquisition_mode_contd_and_start(); + } } - } - } - g_object_unref_t g_object_unref; + void allocate_buffers() + { + for (auto i = 0; i < devices_.size(); ++i) { + const size_t buffer_size = 1 * 1024 * 1024 * 1024; // 1GiB for each + auto n = (buffer_size + devices_[i].u3v_payload_size_ - 1) / devices_[i].u3v_payload_size_; + for (auto j = 0; j < n; ++j) { + auto b = arv_buffer_new_allocate(devices_[i].u3v_payload_size_); + buffers_[i].push_back(b); + arv_stream_push_buffer(devices_[i].stream_, b); + } + log::info("\tDevice/USB {}::{} : {}", i, "Buffer Size", buffer_size); + log::info("\tDevice/USB {}::{} : {}", i, "Number of Buffers", n); + } + } - arv_get_major_version_t arv_get_major_version; - arv_get_minor_version_t arv_get_minor_version; - arv_get_micro_version_t arv_get_micro_version; + void sync_frame_count(std::vector& bufs, int timeout_us) + { + uint32_t max_cnt = 0; + while (true) { + // Update max_cnt + for (int i = 0; i < num_sensor_; ++i) { + if (max_cnt < devices_[i].frame_count_) { + max_cnt = devices_[i].frame_count_; + } + } - arv_get_device_protocol_t arv_get_device_protocol; + // Check all count is same as max_cnt; + bool synchronized = true; + for (int i = 0; i < num_sensor_; ++i) { + synchronized &= devices_[i].frame_count_ == max_cnt; + } - arv_update_device_list_t arv_update_device_list; - arv_get_n_devices_t arv_get_n_devices; + // If it is synchronized, break the loop + if (synchronized) { + break; + } - arv_get_device_id_t arv_get_device_id; - arv_get_device_model_t arv_get_device_model; - arv_get_device_serial_nbr_t arv_get_device_serial_nbr; + // Acquire buffer until cnt is at least max_cnt + for (int i = 0; i < devices_.size(); ++i) { + while (devices_[i].frame_count_ < max_cnt) { + arv_stream_push_buffer(devices_[i].stream_, bufs[i]); + bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); - arv_open_device_t arv_open_device; - arv_device_set_string_feature_value_t arv_device_set_string_feature_value; - arv_device_set_float_feature_value_t arv_device_set_float_feature_value; - arv_device_set_integer_feature_value_t arv_device_set_integer_feature_value; + if (bufs[i] == nullptr) { + log::error("pop_buffer failed when sync frame due to timeout ({}s)", timeout_us * 1e-6f); + throw ::std::runtime_error("buffer is null"); + } + devices_[i].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 + ? static_cast(get_frame_count_from_genDC_descriptor(bufs[i], devices_[i])) + : frame_count_method_ == FrameCountMethod::TIMESTAMP + ? static_cast(arv_buffer_get_timestamp(bufs[i]) & 0x00000000FFFFFFFF) + : -1; + i == 0 ? log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[i].frame_count_, "") : log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[i].frame_count_); + } + } + } + } - arv_device_get_string_feature_value_t arv_device_get_string_feature_value; - arv_device_get_integer_feature_value_t arv_device_get_integer_feature_value; - arv_device_get_float_feature_value_t arv_device_get_float_feature_value; + void consume_old_buffer(std::vector& bufs, int timeout_us = 3 * 1000 * 1000) + { + std::vector N_output_buffers(num_sensor_); + for (auto i = 0; i < num_sensor_; ++i) { + int32_t num_input_buffer; + arv_stream_get_n_buffers(devices_[i].stream_, &num_input_buffer, &(N_output_buffers[i])); + } + // if all stream has N output buffers, discard N-1 of them + for (auto i = 0; i < num_sensor_; ++i) { + for (auto j = 0; j < N_output_buffers[i] - 1; ++j) { + bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); + if (bufs[i] == nullptr) { + log::error("pop_buffer(L2) failed due to timeout ({}s)", timeout_us * 1e-6f); + throw ::std::runtime_error("buffer is null"); + } + devices_[i].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 + ? static_cast(get_frame_count_from_genDC_descriptor(bufs[i], devices_[i])) + : frame_count_method_ == FrameCountMethod::TIMESTAMP + ? static_cast(arv_buffer_get_timestamp(bufs[i]) & 0x00000000FFFFFFFF) + : -1; + i == 0 ? log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20}) [skipped for realtime display]", devices_[i].frame_count_, "") : log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20}) [skipped for realtime display]", "", devices_[i].frame_count_); + arv_stream_push_buffer(devices_[i].stream_, bufs[i]); + } + } + } - arv_device_get_integer_feature_bounds_t arv_device_get_integer_feature_bounds; - arv_device_get_float_feature_bounds_t arv_device_get_float_feature_bounds; + g_object_unref_t g_object_unref; - arv_device_is_feature_available_t arv_device_is_feature_available; - arv_device_dup_register_feature_value_t arv_device_dup_register_feature_value; + arv_get_major_version_t arv_get_major_version; + arv_get_minor_version_t arv_get_minor_version; + arv_get_micro_version_t arv_get_micro_version; - arv_device_create_stream_t arv_device_create_stream; + arv_get_device_protocol_t arv_get_device_protocol; - arv_buffer_new_allocate_t arv_buffer_new_allocate; - arv_stream_push_buffer_t arv_stream_push_buffer; - arv_stream_get_n_buffers_t arv_stream_get_n_buffers; - arv_acquisition_mode_to_string_t arv_acquisition_mode_to_string; - arv_device_execute_command_t arv_device_execute_command; - arv_stream_timeout_pop_buffer_t arv_stream_timeout_pop_buffer; - arv_buffer_get_status_t arv_buffer_get_status; - arv_buffer_get_payload_type_t arv_buffer_get_payload_type; - arv_buffer_get_data_t arv_buffer_get_data; - arv_buffer_get_part_data_t arv_buffer_get_part_data; - arv_buffer_get_timestamp_t arv_buffer_get_timestamp; - arv_device_get_feature_t arv_device_get_feature; + arv_update_device_list_t arv_update_device_list; + arv_get_n_devices_t arv_get_n_devices; - arv_buffer_has_gendc_t arv_buffer_has_gendc; - arv_buffer_get_gendc_descriptor_t arv_buffer_get_gendc_descriptor; + arv_get_device_id_t arv_get_device_id; + arv_get_device_model_t arv_get_device_model; + arv_get_device_serial_nbr_t arv_get_device_serial_nbr; - arv_shutdown_t arv_shutdown; + arv_open_device_t arv_open_device; + arv_device_set_string_feature_value_t arv_device_set_string_feature_value; + arv_device_set_float_feature_value_t arv_device_set_float_feature_value; + arv_device_set_integer_feature_value_t arv_device_set_integer_feature_value; - arv_camera_new_t arv_camera_new; - arv_camera_get_device_t arv_camera_get_device; - arv_camera_create_stream_t arv_camera_create_stream; + arv_device_get_string_feature_value_t arv_device_get_string_feature_value; + arv_device_get_integer_feature_value_t arv_device_get_integer_feature_value; + arv_device_get_float_feature_value_t arv_device_get_float_feature_value; - arv_fake_device_new_t arv_fake_device_new; - arv_enable_interface_t arv_enable_interface; - arv_set_fake_camera_genicam_filename_t arv_set_fake_camera_genicam_filename; - arv_fake_device_get_fake_camera_t arv_fake_device_get_fake_camera; + arv_device_get_integer_feature_bounds_t arv_device_get_integer_feature_bounds; + arv_device_get_float_feature_bounds_t arv_device_get_float_feature_bounds; - arv_uv_device_set_usb_mode_t arv_uv_device_set_usb_mode; + arv_device_is_feature_available_t arv_device_is_feature_available; + arv_device_dup_register_feature_value_t arv_device_dup_register_feature_value; - static std::map> instances_; + arv_device_create_stream_t arv_device_create_stream; - int32_t num_sensor_; //SENSOR NUMBER + arv_buffer_new_allocate_t arv_buffer_new_allocate; + arv_stream_push_buffer_t arv_stream_push_buffer; + arv_stream_get_n_buffers_t arv_stream_get_n_buffers; + arv_acquisition_mode_to_string_t arv_acquisition_mode_to_string; + arv_device_execute_command_t arv_device_execute_command; + arv_stream_timeout_pop_buffer_t arv_stream_timeout_pop_buffer; + arv_buffer_get_status_t arv_buffer_get_status; + arv_buffer_get_payload_type_t arv_buffer_get_payload_type; + arv_buffer_get_data_t arv_buffer_get_data; + arv_buffer_get_part_data_t arv_buffer_get_part_data; + arv_buffer_get_timestamp_t arv_buffer_get_timestamp; + arv_device_get_feature_t arv_device_get_feature; - DynamicModule gobject_; - DynamicModule aravis_; - GError *err_ = nullptr; + arv_buffer_has_gendc_t arv_buffer_has_gendc; + arv_buffer_get_gendc_descriptor_t arv_buffer_get_gendc_descriptor; - bool frame_sync_; - bool realtime_display_mode_; - bool is_gendc_; - bool is_param_integer_; - int32_t operation_mode_; + arv_shutdown_t arv_shutdown; - uint32_t frame_cnt_; - int32_t device_idx_; //USB DEVICE INDEX - int frame_count_method_; + arv_camera_new_t arv_camera_new; + arv_camera_get_device_t arv_camera_get_device; + arv_camera_create_stream_t arv_camera_create_stream; - // genDC - ContainerHeader gendc_descriptor_; + arv_fake_device_new_t arv_fake_device_new; + arv_enable_interface_t arv_enable_interface; + arv_set_fake_camera_genicam_filename_t arv_set_fake_camera_genicam_filename; + arv_fake_device_get_fake_camera_t arv_fake_device_get_fake_camera; - std::string pixel_format_; + arv_uv_device_set_usb_mode_t arv_uv_device_set_usb_mode; - std::vector devices_; //USB DEVICE + static std::map> instances_; - std::vector > buffers_; + int32_t num_sensor_; // SENSOR NUMBER - bool disposed_; - bool sim_mode_; + DynamicModule gobject_; + DynamicModule aravis_; + GError* err_ = nullptr; - bool order_filp_; + bool frame_sync_; + bool realtime_display_mode_; + bool is_gendc_; + bool is_param_integer_; + int32_t operation_mode_; -}; // class U3V + uint32_t frame_cnt_; + int32_t device_idx_; // USB DEVICE INDEX + int frame_count_method_; -std::map> U3V::instances_; + // genDC + ContainerHeader gendc_descriptor_; + std::string pixel_format_; -class U3VFakeCam : public U3V{ -public: - static U3V & get_instance(const std::string& id, - int32_t num_sensor, - int32_t width = 640, - int32_t height = 480, - float_t fps = 25.0, - const std::string& pixel_format = "Mono8" - ) - { - if (instances_.count(id) == 0) { - ion::log::info("Create U3VFakeCam U3V instance: {}", id); - instances_[id] = std::unique_ptr(new U3VFakeCam(num_sensor, width, height, fps, pixel_format)); - } + std::vector devices_; // USB DEVICE - return *instances_[id].get(); - } - void get(std::vector>& outs) override { - auto timeout_us = 30 * 1000 * 1000; - std::vector bufs(num_sensor_); - for (int i = 0;i< num_sensor_;i++){ - auto size = devices_[i].u3v_payload_size_; - arv_stream_push_buffer (devices_[i].stream_, arv_buffer_new_allocate (size)); - bufs[i] = arv_stream_timeout_pop_buffer (devices_[i].stream_, timeout_us); - if (bufs[i] == nullptr){ - log::error("pop_buffer(L1) failed due to timeout ({}s)", timeout_us*1e-6f); - throw ::std::runtime_error("Buffer is null"); - } - devices_[i].frame_count_ += 1; - memcpy(outs[i].data(), arv_buffer_get_part_data(bufs[i], 0, nullptr), size); - } - } + std::vector> buffers_; -private: - U3VFakeCam(int32_t num_sensor, int32_t width, int32_t height , float_t fps, const std::string & pixel_format, char* dev_id = nullptr) - : U3V(num_sensor, false, false, true, width, height , fps, pixel_format, nullptr){ - open_fake_devices(width, height, fps, pixel_format); + bool disposed_; + bool sim_mode_; - // Start streaming and start acquisition - for (auto i=0; i(new U3VRealCam(num_sensor, frame_sync, realtime_display_mode, sim_mode, width, height, fps, pixel_format)); - } + }; // class U3V - return *instances_[id].get(); - } + std::map> U3V::instances_; - void get(std::vector>& outs) override{ - auto timeout_us = 30 * 1000 * 1000; - int32_t num_device = devices_.size(); - if (sim_mode_){ - std::vector bufs(num_device); - for (int i = 0;i< num_device;i++){ + class U3VFakeCam : public U3V { + public: + static U3V& get_instance(const std::string& id, + int32_t num_sensor, + int32_t width = 640, + int32_t height = 480, + float_t fps = 25.0, + const std::string& pixel_format = "Mono8") + { + if (instances_.count(id) == 0) { + ion::log::info("Create U3VFakeCam U3V instance: {}", id); + instances_[id] = std::unique_ptr(new U3VFakeCam(num_sensor, width, height, fps, pixel_format)); + } + + return *instances_[id].get(); + } + void get(std::vector>& outs) override + { + auto timeout_us = 30 * 1000 * 1000; + std::vector bufs(num_sensor_); + for (int i = 0; i < num_sensor_; i++) { auto size = devices_[i].u3v_payload_size_; - arv_stream_push_buffer (devices_[i].stream_, arv_buffer_new_allocate (size)); - bufs[i] = arv_stream_timeout_pop_buffer (devices_[i].stream_, timeout_us); - if (bufs[i] == nullptr){ - log::error("pop_buffer(L1) failed due to timeout ({}s)", timeout_us*1e-6f); + arv_stream_push_buffer(devices_[i].stream_, arv_buffer_new_allocate(size)); + bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); + if (bufs[i] == nullptr) { + log::error("pop_buffer(L1) failed due to timeout ({}s)", timeout_us * 1e-6f); throw ::std::runtime_error("Buffer is null"); } devices_[i].frame_count_ += 1; memcpy(outs[i].data(), arv_buffer_get_part_data(bufs[i], 0, nullptr), size); } - }else { + } - std::vector bufs(num_device); + private: + U3VFakeCam(int32_t num_sensor, int32_t width, int32_t height, float_t fps, const std::string& pixel_format, char* dev_id = nullptr) + : U3V(num_sensor, false, false, true, width, height, fps, pixel_format, nullptr) + { + open_fake_devices(width, height, fps, pixel_format); - // default is OperationMode::Came1USB1 - if (operation_mode_ == OperationMode::Came2USB2 || operation_mode_ == OperationMode::Came1USB1) { + // Start streaming and start acquisition + for (auto i = 0; i < devices_.size(); ++i) { + devices_[i].stream_ = arv_device_create_stream(devices_[i].device_, NULL, NULL, &err_); + } - // if aravis output queue length is more than N (where N > 1) for all devices, pop all N-1 buffer - if (realtime_display_mode_) { - consume_old_buffer(bufs,timeout_us); + for (auto i = 0; i < devices_.size(); ++i) { + arv_device_execute_command(devices_[i].device_, "AcquisitionStart", &err_); + log::info("\tFake Device {}::{} : {}", i, "Command", "AcquisitionStart"); + } + }; + }; + + class U3VRealCam : public U3V { + public: + static U3V& get_instance(const std::string& id, + int32_t num_sensor, + bool frame_sync, + bool realtime_display_mode, + bool sim_mode = false, + int32_t width = 640, + int32_t height = 480, + float_t fps = 25.0, + const std::string& pixel_format = "Mono8") + { + if (instances_.count(id) == 0) { + ion::log::info("Create U3VRealCam instance: {}", id); + instances_[id] = std::unique_ptr(new U3VRealCam(num_sensor, frame_sync, realtime_display_mode, sim_mode, width, height, fps, pixel_format)); } - // get the first buffer for each stream - for (auto i = 0; i >& outs) override + { + auto timeout_us = 30 * 1000 * 1000; + int32_t num_device = devices_.size(); + if (sim_mode_) { + std::vector bufs(num_device); + for (int i = 0; i < num_device; i++) { + auto size = devices_[i].u3v_payload_size_; + arv_stream_push_buffer(devices_[i].stream_, arv_buffer_new_allocate(size)); + bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); + if (bufs[i] == nullptr) { + log::error("pop_buffer(L1) failed due to timeout ({}s)", timeout_us * 1e-6f); + throw ::std::runtime_error("Buffer is null"); + } + devices_[i].frame_count_ += 1; + memcpy(outs[i].data(), arv_buffer_get_part_data(bufs[i], 0, nullptr), size); } - devices_[i].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 - ? static_cast(get_frame_count_from_genDC_descriptor(bufs[i], devices_[i])) - : frame_count_method_ == FrameCountMethod::TIMESTAMP - ? static_cast(arv_buffer_get_timestamp(bufs[i]) & 0x00000000FFFFFFFF) - : -1; - i == 0 ? - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[i].frame_count_, "") : - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[i].frame_count_); - } + } else { - if (frame_sync_) { - sync_frame_count(bufs,timeout_us); - } + std::vector bufs(num_device); - for (int i = 0; i < num_device; ++i) { - auto sz = (std::min)(devices_[i].image_payload_size_, static_cast(outs[i].size_in_bytes())); - ::memcpy(outs[i].data(), arv_buffer_get_part_data(bufs[i], 0, nullptr), sz); - arv_stream_push_buffer(devices_[i].stream_, bufs[i]); - log::trace("Obtained Frame from USB{}: {}", i, devices_[i].frame_count_); - } + // default is OperationMode::Came1USB1 + if (operation_mode_ == OperationMode::Came2USB2 || operation_mode_ == OperationMode::Came1USB1) { - } else if (operation_mode_ == OperationMode::Came1USB2) { + // if aravis output queue length is more than N (where N > 1) for all devices, pop all N-1 buffer + if (realtime_display_mode_) { + consume_old_buffer(bufs, timeout_us); + } - uint32_t latest_cnt = 0; - int32_t min_frame_device_idx = 0; + // get the first buffer for each stream + for (auto i = 0; i < num_device; ++i) { + bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); + if (bufs[i] == nullptr) { + log::error("pop_buffer(L1) failed due to timeout ({}s)", timeout_us * 1e-6f); + throw ::std::runtime_error("Buffer is null"); + } + devices_[i].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 + ? static_cast(get_frame_count_from_genDC_descriptor(bufs[i], devices_[i])) + : frame_count_method_ == FrameCountMethod::TIMESTAMP + ? static_cast(arv_buffer_get_timestamp(bufs[i]) & 0x00000000FFFFFFFF) + : -1; + i == 0 ? log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[i].frame_count_, "") : log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[i].frame_count_); + } - // if aravis output queue length is more than N (where N > 1) for all devices, pop all N-1 buffer - if (realtime_display_mode_) { - consume_old_buffer(bufs,timeout_us); - } + if (frame_sync_) { + sync_frame_count(bufs, timeout_us); + } - //first buffer - device_idx_ = (device_idx_ + 1) >= num_device ? 0 : device_idx_ + 1; - bufs[device_idx_] = arv_stream_timeout_pop_buffer(devices_[device_idx_].stream_, 30 * 1000 * 1000); - if (bufs[device_idx_] == nullptr) { - log::error("pop_buffer(L4) failed due to timeout ({}s)", timeout_us * 1e-6f); - throw ::std::runtime_error("buffer is null"); - } - devices_[device_idx_].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 - ? static_cast(get_frame_count_from_genDC_descriptor(bufs[device_idx_], devices_[device_idx_])) - : frame_count_method_ == FrameCountMethod::TIMESTAMP - ? static_cast(arv_buffer_get_timestamp(bufs[device_idx_]) & 0x00000000FFFFFFFF) - : -1; - latest_cnt = devices_[device_idx_].frame_count_; - device_idx_ == 0 ? - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[device_idx_].frame_count_, "") : - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[device_idx_].frame_count_); - - int internal_count = 0; - int max_internal_count = 1000; - - while (frame_cnt_ >= latest_cnt) { - arv_stream_push_buffer(devices_[device_idx_].stream_, bufs[device_idx_]); - bufs[device_idx_] = arv_stream_timeout_pop_buffer (devices_[device_idx_].stream_, 30 * 1000 * 1000); - if (bufs[device_idx_] == nullptr){ - log::error("pop_buffer(L4) failed due to timeout ({}s)", timeout_us*1e-6f); - throw ::std::runtime_error("buffer is null"); - } - devices_[device_idx_].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 - ? static_cast(get_frame_count_from_genDC_descriptor(bufs[device_idx_], devices_[device_idx_])) - : frame_count_method_ == FrameCountMethod::TIMESTAMP - ? static_cast(arv_buffer_get_timestamp(bufs[device_idx_]) & 0x00000000FFFFFFFF) - : -1; - latest_cnt = devices_[device_idx_].frame_count_; - device_idx_ == 0 ? - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[device_idx_].frame_count_, "") : - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[device_idx_].frame_count_); - - if (internal_count++ > max_internal_count){ - log::error("pop_buffer(L9) The sequential invalid buffer is more than {}; Stop the pipeline.", max_internal_count); - throw ::std::runtime_error("Invalid framecount"); - } - } + for (int i = 0; i < num_device; ++i) { + auto sz = (std::min)(devices_[i].image_payload_size_, static_cast(outs[i].size_in_bytes())); + ::memcpy(outs[i].data(), arv_buffer_get_part_data(bufs[i], 0, nullptr), sz); + arv_stream_push_buffer(devices_[i].stream_, bufs[i]); + log::trace("Obtained Frame from USB{}: {}", i, devices_[i].frame_count_); + } - frame_cnt_ = latest_cnt; - auto sz = (std::min)(devices_[device_idx_].image_payload_size_, static_cast(outs[0].size_in_bytes())); - ::memcpy(outs[0].data(), arv_buffer_get_part_data(bufs[device_idx_], 0, nullptr), sz); - arv_stream_push_buffer(devices_[device_idx_].stream_, bufs[device_idx_]); + } else if (operation_mode_ == OperationMode::Came1USB2) { - log::trace("Obtained Frame from USB{}: {}", device_idx_, frame_cnt_); - } - } - } -private: - U3VRealCam(int32_t num_sensor, bool frame_sync, bool realtime_display_mode, bool sim_mode, int32_t width, int32_t height , float_t fps, const std::string & pixel_format, char* dev_id = nullptr) - : U3V(num_sensor, frame_sync, realtime_display_mode, sim_mode, width, height , fps, pixel_format, nullptr){ - // check if the camera is available - arv_update_device_list(); - auto num_device = arv_get_n_devices (); - if (num_device == 0){ - log::warn("Fallback to simulation mode: Could not find camera"); - sim_mode_ = true; - } - if (sim_mode_){ - open_fake_devices(width, height, fps, pixel_format); - - // Start streaming and start acquisition - create_stream_and_start_acquisition(order_filp_); - }else{ - // Real Camera - validate_user_input(num_device, dev_id); - open_real_devices(num_device, num_sensor_, dev_id); - create_stream_and_start_acquisition(order_filp_); - allocate_buffers(); - } - }; -}; - - -class U3VGenDC: public U3V{ -public: - static U3V & get_instance(const std::string& id, - int32_t num_sensor, - bool frame_sync, - bool realtime_display_mode, - bool sim_mode = false, - int32_t width = 640, - int32_t height = 480, - float_t fps = 25.0, - const std::string& pixel_format = "Mono8" - ) - { - if (instances_.count(id) == 0) { - ion::log::info("Create GenDC instance: {}", id); - instances_[id] = std::unique_ptr(new U3VGenDC(num_sensor, frame_sync, realtime_display_mode, sim_mode, width, height, fps, pixel_format)); - } + uint32_t latest_cnt = 0; + int32_t min_frame_device_idx = 0; - return *instances_[id].get(); - } + // if aravis output queue length is more than N (where N > 1) for all devices, pop all N-1 buffer + if (realtime_display_mode_) { + consume_old_buffer(bufs, timeout_us); + } + // first buffer + device_idx_ = (device_idx_ + 1) >= num_device ? 0 : device_idx_ + 1; + bufs[device_idx_] = arv_stream_timeout_pop_buffer(devices_[device_idx_].stream_, 30 * 1000 * 1000); + if (bufs[device_idx_] == nullptr) { + log::error("pop_buffer(L4) failed due to timeout ({}s)", timeout_us * 1e-6f); + throw ::std::runtime_error("buffer is null"); + } + devices_[device_idx_].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 + ? static_cast(get_frame_count_from_genDC_descriptor(bufs[device_idx_], devices_[device_idx_])) + : frame_count_method_ == FrameCountMethod::TIMESTAMP + ? static_cast(arv_buffer_get_timestamp(bufs[device_idx_]) & 0x00000000FFFFFFFF) + : -1; + latest_cnt = devices_[device_idx_].frame_count_; + device_idx_ == 0 ? log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[device_idx_].frame_count_, "") : log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[device_idx_].frame_count_); + + int internal_count = 0; + int max_internal_count = 1000; + + while (frame_cnt_ >= latest_cnt) { + arv_stream_push_buffer(devices_[device_idx_].stream_, bufs[device_idx_]); + bufs[device_idx_] = arv_stream_timeout_pop_buffer(devices_[device_idx_].stream_, 30 * 1000 * 1000); + if (bufs[device_idx_] == nullptr) { + log::error("pop_buffer(L4) failed due to timeout ({}s)", timeout_us * 1e-6f); + throw ::std::runtime_error("buffer is null"); + } + devices_[device_idx_].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 + ? static_cast(get_frame_count_from_genDC_descriptor(bufs[device_idx_], devices_[device_idx_])) + : frame_count_method_ == FrameCountMethod::TIMESTAMP + ? static_cast(arv_buffer_get_timestamp(bufs[device_idx_]) & 0x00000000FFFFFFFF) + : -1; + latest_cnt = devices_[device_idx_].frame_count_; + device_idx_ == 0 ? log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[device_idx_].frame_count_, "") : log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[device_idx_].frame_count_); + + if (internal_count++ > max_internal_count) { + log::error("pop_buffer(L9) The sequential invalid buffer is more than {}; Stop the pipeline.", max_internal_count); + throw ::std::runtime_error("Invalid framecount"); + } + } - void get(std::vector& outs) override{ - // TODO: Is 3 second fine? - auto timeout_us = 3 * 1000 * 1000; + frame_cnt_ = latest_cnt; + auto sz = (std::min)(devices_[device_idx_].image_payload_size_, static_cast(outs[0].size_in_bytes())); + ::memcpy(outs[0].data(), arv_buffer_get_part_data(bufs[device_idx_], 0, nullptr), sz); + arv_stream_push_buffer(devices_[device_idx_].stream_, bufs[device_idx_]); - int32_t num_device = devices_.size(); - std::vector bufs(num_device); - if (sim_mode_){ - std::vector bufs(num_sensor_); - for (int i = 0;i< num_sensor_;i++){ - auto size = devices_[i].u3v_payload_size_; - arv_stream_push_buffer (devices_[i].stream_, arv_buffer_new_allocate (size)); - bufs[i] = arv_stream_timeout_pop_buffer (devices_[i].stream_, timeout_us); - if (bufs[i] == nullptr){ - log::error("pop_buffer(L1) failed due to timeout ({}s)", timeout_us*1e-6f); - throw ::std::runtime_error("Buffer is null"); + log::trace("Obtained Frame from USB{}: {}", device_idx_, frame_cnt_); } - devices_[i].frame_count_ += 1; - memcpy(outs[i], arv_buffer_get_part_data(bufs[i], 0, nullptr), size);} - } - else if (operation_mode_ == OperationMode::Came2USB2 || operation_mode_ == OperationMode::Came1USB1){ - - if (realtime_display_mode_){ - consume_old_buffer(bufs,timeout_us); + } } - // get the first buffer for each stream - for (auto i = 0; i< devices_.size(); ++i) { - bufs[i] = arv_stream_timeout_pop_buffer (devices_[i].stream_, timeout_us); - if (bufs[i] == nullptr){ - log::error("pop_buffer(L5) failed due to timeout ({}s)", timeout_us*1e-6f); - throw ::std::runtime_error("buffer is null"); + private: + U3VRealCam(int32_t num_sensor, bool frame_sync, bool realtime_display_mode, bool sim_mode, int32_t width, int32_t height, float_t fps, const std::string& pixel_format, char* dev_id = nullptr) + : U3V(num_sensor, frame_sync, realtime_display_mode, sim_mode, width, height, fps, pixel_format, nullptr) + { + // check if the camera is available + arv_update_device_list(); + auto num_device = arv_get_n_devices(); + if (num_device == 0) { + log::warn("Fallback to simulation mode: Could not find camera"); + sim_mode_ = true; + } + if (sim_mode_) { + open_fake_devices(width, height, fps, pixel_format); + + // Start streaming and start acquisition + create_stream_and_start_acquisition(order_filp_); + } else { + // Real Camera + validate_user_input(num_device, dev_id); + open_real_devices(num_device, num_sensor_, dev_id); + create_stream_and_start_acquisition(order_filp_); + allocate_buffers(); + } + }; + }; + + class U3VGenDC : public U3V { + public: + static U3V& get_instance(const std::string& id, + int32_t num_sensor, + bool frame_sync, + bool realtime_display_mode, + bool sim_mode = false, + int32_t width = 640, + int32_t height = 480, + float_t fps = 25.0, + const std::string& pixel_format = "Mono8") + { + if (instances_.count(id) == 0) { + ion::log::info("Create GenDC instance: {}", id); + instances_[id] = std::unique_ptr(new U3VGenDC(num_sensor, frame_sync, realtime_display_mode, sim_mode, width, height, fps, pixel_format)); } - devices_[i].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 - ? static_cast(get_frame_count_from_genDC_descriptor(bufs[i], devices_[i])) - : frame_count_method_ == FrameCountMethod::TIMESTAMP - ? static_cast(arv_buffer_get_timestamp(bufs[i]) & 0x00000000FFFFFFFF) - : -1; - - i == 0 ? - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[i].frame_count_, "") : - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[i].frame_count_); - } - if (frame_sync_) { - sync_frame_count(bufs,timeout_us); + return *instances_[id].get(); } - for (int i = 0; i < num_sensor_; ++i){ - ::memcpy(outs[i], arv_buffer_get_data(bufs[i], nullptr), devices_[i].u3v_payload_size_); - // ::memcpy(outs[i*num_sensor_+1], &(devices_[i].header_info_), sizeof(ion::bb::image_io::rawHeader)); - arv_stream_push_buffer(devices_[i].stream_, bufs[i]); - log::trace("Obtained Frame from USB{}: {}", i, devices_[i].frame_count_); - } - } else if (operation_mode_ == OperationMode::Came1USB2) { - uint32_t latest_cnt = 0; - int32_t min_frame_device_idx = 0; + void get(std::vector& outs) override + { + // TODO: Is 3 second fine? + auto timeout_us = 3 * 1000 * 1000; + + int32_t num_device = devices_.size(); + std::vector bufs(num_device); + if (sim_mode_) { + std::vector bufs(num_sensor_); + for (int i = 0; i < num_sensor_; i++) { + auto size = devices_[i].u3v_payload_size_; + arv_stream_push_buffer(devices_[i].stream_, arv_buffer_new_allocate(size)); + bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); + if (bufs[i] == nullptr) { + log::error("pop_buffer(L1) failed due to timeout ({}s)", timeout_us * 1e-6f); + throw ::std::runtime_error("Buffer is null"); + } + devices_[i].frame_count_ += 1; + memcpy(outs[i], arv_buffer_get_part_data(bufs[i], 0, nullptr), size); + } + } else if (operation_mode_ == OperationMode::Came2USB2 || operation_mode_ == OperationMode::Came1USB1) { - // if aravis output queue length is more than N (where N > 1) for all devices, pop all N-1 buffer - if (realtime_display_mode_){ - consume_old_buffer(bufs,timeout_us); - } + if (realtime_display_mode_) { + consume_old_buffer(bufs, timeout_us); + } - //first buffer - device_idx_ = (device_idx_+1) >= num_device ? 0 : device_idx_+1; - bufs[device_idx_] = arv_stream_timeout_pop_buffer (devices_[device_idx_].stream_, 30 * 1000 * 1000); - if (bufs[device_idx_] == nullptr){ - log::error("pop_buffer(L4) failed due to timeout ({}s)", timeout_us*1e-6f); - throw ::std::runtime_error("buffer is null"); - } - devices_[device_idx_].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 - ? static_cast(get_frame_count_from_genDC_descriptor(bufs[device_idx_], devices_[device_idx_])) - : frame_count_method_ == FrameCountMethod::TIMESTAMP - ? static_cast(arv_buffer_get_timestamp(bufs[device_idx_]) & 0x00000000FFFFFFFF) - : -1; - latest_cnt = devices_[device_idx_].frame_count_; - device_idx_ == 0 ? - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[device_idx_].frame_count_, "") : - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[device_idx_].frame_count_); - - int internal_count = 0; - int max_internal_count = 1000; - - while (frame_cnt_ >= latest_cnt) { - arv_stream_push_buffer(devices_[device_idx_].stream_, bufs[device_idx_]); - auto timeout2_us = 30 * 1000 * 1000; - bufs[device_idx_] = arv_stream_timeout_pop_buffer (devices_[device_idx_].stream_, timeout2_us); - if (bufs[device_idx_] == nullptr){ - log::error("pop_buffer(L8) failed due to timeout ({}s)", timeout2_us*1e-6f); + // get the first buffer for each stream + for (auto i = 0; i < devices_.size(); ++i) { + bufs[i] = arv_stream_timeout_pop_buffer(devices_[i].stream_, timeout_us); + if (bufs[i] == nullptr) { + log::error("pop_buffer(L5) failed due to timeout ({}s)", timeout_us * 1e-6f); throw ::std::runtime_error("buffer is null"); + } + devices_[i].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 + ? static_cast(get_frame_count_from_genDC_descriptor(bufs[i], devices_[i])) + : frame_count_method_ == FrameCountMethod::TIMESTAMP + ? static_cast(arv_buffer_get_timestamp(bufs[i]) & 0x00000000FFFFFFFF) + : -1; + + i == 0 ? log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[i].frame_count_, "") : log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[i].frame_count_); + } + + if (frame_sync_) { + sync_frame_count(bufs, timeout_us); + } + + for (int i = 0; i < num_sensor_; ++i) { + ::memcpy(outs[i], arv_buffer_get_data(bufs[i], nullptr), devices_[i].u3v_payload_size_); + // ::memcpy(outs[i*num_sensor_+1], &(devices_[i].header_info_), sizeof(ion::bb::image_io::rawHeader)); + arv_stream_push_buffer(devices_[i].stream_, bufs[i]); + log::trace("Obtained Frame from USB{}: {}", i, devices_[i].frame_count_); + } + } else if (operation_mode_ == OperationMode::Came1USB2) { + uint32_t latest_cnt = 0; + int32_t min_frame_device_idx = 0; + + // if aravis output queue length is more than N (where N > 1) for all devices, pop all N-1 buffer + if (realtime_display_mode_) { + consume_old_buffer(bufs, timeout_us); + } + + // first buffer + device_idx_ = (device_idx_ + 1) >= num_device ? 0 : device_idx_ + 1; + bufs[device_idx_] = arv_stream_timeout_pop_buffer(devices_[device_idx_].stream_, 30 * 1000 * 1000); + if (bufs[device_idx_] == nullptr) { + log::error("pop_buffer(L4) failed due to timeout ({}s)", timeout_us * 1e-6f); + throw ::std::runtime_error("buffer is null"); } devices_[device_idx_].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 ? static_cast(get_frame_count_from_genDC_descriptor(bufs[device_idx_], devices_[device_idx_])) : frame_count_method_ == FrameCountMethod::TIMESTAMP ? static_cast(arv_buffer_get_timestamp(bufs[device_idx_]) & 0x00000000FFFFFFFF) : -1; - device_idx_ == 0 ? - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[device_idx_].frame_count_, "") : - log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[device_idx_].frame_count_); latest_cnt = devices_[device_idx_].frame_count_; - if (internal_count++ > max_internal_count){ - log::error("pop_buffer(L10) The sequential invalid buffer is more than {}; Stop the pipeline.", max_internal_count); - throw ::std::runtime_error("Invalid framecount"); + device_idx_ == 0 ? log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[device_idx_].frame_count_, "") : log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[device_idx_].frame_count_); + + int internal_count = 0; + int max_internal_count = 1000; + + while (frame_cnt_ >= latest_cnt) { + arv_stream_push_buffer(devices_[device_idx_].stream_, bufs[device_idx_]); + auto timeout2_us = 30 * 1000 * 1000; + bufs[device_idx_] = arv_stream_timeout_pop_buffer(devices_[device_idx_].stream_, timeout2_us); + if (bufs[device_idx_] == nullptr) { + log::error("pop_buffer(L8) failed due to timeout ({}s)", timeout2_us * 1e-6f); + throw ::std::runtime_error("buffer is null"); + } + devices_[device_idx_].frame_count_ = frame_count_method_ == FrameCountMethod::TYPESPECIFIC3 + ? static_cast(get_frame_count_from_genDC_descriptor(bufs[device_idx_], devices_[device_idx_])) + : frame_count_method_ == FrameCountMethod::TIMESTAMP + ? static_cast(arv_buffer_get_timestamp(bufs[device_idx_]) & 0x00000000FFFFFFFF) + : -1; + device_idx_ == 0 ? log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", devices_[device_idx_].frame_count_, "") : log::trace("All-Popped Frames (USB0, USB1)=({:20}, {:20})", "", devices_[device_idx_].frame_count_); + latest_cnt = devices_[device_idx_].frame_count_; + if (internal_count++ > max_internal_count) { + log::error("pop_buffer(L10) The sequential invalid buffer is more than {}; Stop the pipeline.", max_internal_count); + throw ::std::runtime_error("Invalid framecount"); + } } - } - frame_cnt_ = latest_cnt; - ::memcpy(outs[0], arv_buffer_get_data(bufs[device_idx_], nullptr), devices_[device_idx_].u3v_payload_size_); - // ::memcpy(outs[1], &(devices_[device_idx_].header_info_), sizeof(ion::bb::image_io::rawHeader)); - arv_stream_push_buffer(devices_[device_idx_].stream_, bufs[device_idx_]); - log::trace("Obtained Frame from USB{}: {}", device_idx_, frame_cnt_); - } - } -private: - U3VGenDC(int32_t num_sensor, bool frame_sync, bool realtime_display_mode, bool sim_mode, int32_t width, int32_t height , float_t fps, const std::string & pixel_format, char* dev_id = nullptr) - : U3V(num_sensor, frame_sync, realtime_display_mode, sim_mode, width, height , fps, pixel_format, nullptr){ - // check if the camera is available - arv_update_device_list(); - auto num_device = arv_get_n_devices (); - if (num_device == 0){ - log::warn("Fallback to simulation mode: Could not find camera"); - sim_mode_ = true; - } - if (sim_mode_){ - open_fake_devices(width, height, fps, pixel_format); - - // Start streaming and start acquisition - create_stream_and_start_acquisition(order_filp_); - }else{ - // Real Camera - validate_user_input(num_device, dev_id); - open_real_devices(num_device, num_sensor_, dev_id); - create_stream_and_start_acquisition(order_filp_); - allocate_buffers(); - } - }; + frame_cnt_ = latest_cnt; + ::memcpy(outs[0], arv_buffer_get_data(bufs[device_idx_], nullptr), devices_[device_idx_].u3v_payload_size_); + // ::memcpy(outs[1], &(devices_[device_idx_].header_info_), sizeof(ion::bb::image_io::rawHeader)); + arv_stream_push_buffer(devices_[device_idx_].stream_, bufs[device_idx_]); + log::trace("Obtained Frame from USB{}: {}", device_idx_, frame_cnt_); + } + } -}; + private: + U3VGenDC(int32_t num_sensor, bool frame_sync, bool realtime_display_mode, bool sim_mode, int32_t width, int32_t height, float_t fps, const std::string& pixel_format, char* dev_id = nullptr) + : U3V(num_sensor, frame_sync, realtime_display_mode, sim_mode, width, height, fps, pixel_format, nullptr) + { + // check if the camera is available + arv_update_device_list(); + auto num_device = arv_get_n_devices(); + if (num_device == 0) { + log::warn("Fallback to simulation mode: Could not find camera"); + sim_mode_ = true; + } + if (sim_mode_) { + open_fake_devices(width, height, fps, pixel_format); + + // Start streaming and start acquisition + create_stream_and_start_acquisition(order_filp_); + } else { + // Real Camera + validate_user_input(num_device, dev_id); + open_real_devices(num_device, num_sensor_, dev_id); + create_stream_and_start_acquisition(order_filp_); + allocate_buffers(); + } + }; + }; -} // namespace image_io -} // namespace bb -} // namespace ion + } // namespace image_io +} // namespace bb +} // namespace ion -extern "C" -int ION_EXPORT u3v_dispose(const char *id) { +extern "C" int ION_EXPORT u3v_dispose(const char* id) +{ ion::bb::image_io::U3V::release_instance(id); return 0; } @@ -1398,19 +1411,18 @@ int u3v_camera_frame_count( halide_buffer_t* out) { try { - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, num_sensor, frame_sync, realtime_display_mode)); - std::vector obufs{out->host}; + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, num_sensor, frame_sync, realtime_display_mode)); + std::vector obufs { out->host }; if (out->is_bounds_query()) { out->dim[0].min = 0; out->dim[0].extent = num_sensor; return 0; - } - else { + } else { u3v.get_frame_count(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("frame_count"); ion::log::error("Exception was thrown: {}", e.what()); return 1; @@ -1420,33 +1432,32 @@ int u3v_camera_frame_count( } } -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_camera1( +extern "C" int ION_EXPORT ion_bb_image_io_u3v_camera1( bool frame_sync, bool realtime_display_mode, double gain0, double exposure0, - halide_buffer_t * id_buf, halide_buffer_t * gain_key_buf, halide_buffer_t * exposure_key_buf, - halide_buffer_t * out0) + halide_buffer_t* id_buf, halide_buffer_t* gain_key_buf, halide_buffer_t* exposure_key_buf, + halide_buffer_t* out0) { using namespace Halide; try { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); const std::string gain_key(reinterpret_cast(gain_key_buf->host)); const std::string exposure_key(reinterpret_cast(exposure_key_buf->host)); - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 1, frame_sync, realtime_display_mode)); + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 1, frame_sync, realtime_display_mode)); if (out0->is_bounds_query()) { - //bounds query + // bounds query return 0; - }else{ + } else { // set gain & exposure u3v.set_gain(0, gain_key, gain0); u3v.set_exposure(0, exposure_key, exposure0); - std::vector > obufs{Halide::Buffer<>(*out0)}; + std::vector> obufs { Halide::Buffer<>(*out0) }; u3v.get(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -1456,33 +1467,32 @@ int ION_EXPORT ion_bb_image_io_u3v_camera1( } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_camera1); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_camera2( +extern "C" int ION_EXPORT ion_bb_image_io_u3v_camera2( bool frame_sync, bool realtime_display_mode, double gain0, double gain1, double exposure0, double exposure1, - halide_buffer_t * id_buf, halide_buffer_t * gain_key_buf, halide_buffer_t * exposure_key_buf, - halide_buffer_t * out0, halide_buffer_t * out1) + halide_buffer_t* id_buf, halide_buffer_t* gain_key_buf, halide_buffer_t* exposure_key_buf, + halide_buffer_t* out0, halide_buffer_t* out1) { using namespace Halide; try { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); const std::string gain_key(reinterpret_cast(gain_key_buf->host)); const std::string exposure_key(reinterpret_cast(exposure_key_buf->host)); - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 2, frame_sync, realtime_display_mode)); + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 2, frame_sync, realtime_display_mode)); if (out0->is_bounds_query() || out1->is_bounds_query()) { - //bounds query + // bounds query return 0; - }else{ + } else { // set gain & exposure u3v.set_gain(0, gain_key, gain0); u3v.set_gain(1, gain_key, gain1); u3v.set_exposure(0, exposure_key, exposure0); u3v.set_exposure(1, exposure_key, exposure1); - std::vector > obufs{Halide::Buffer<>(*out0), Halide::Buffer<>(*out1)}; + std::vector> obufs { Halide::Buffer<>(*out0), Halide::Buffer<>(*out1) }; u3v.get(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -1492,63 +1502,59 @@ int ION_EXPORT ion_bb_image_io_u3v_camera2( } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_camera2); - -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_camera1_frame_count( - halide_buffer_t *, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_camera1_frame_count( + halide_buffer_t*, int32_t num_sensor, bool frame_sync, bool realtime_display_mode, - halide_buffer_t * id_buf, halide_buffer_t* out) + halide_buffer_t* id_buf, halide_buffer_t* out) { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); return u3v_camera_frame_count(id, num_sensor, frame_sync, realtime_display_mode, out); } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_camera1_frame_count); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_camera2_frame_count( - halide_buffer_t *, - halide_buffer_t *, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_camera2_frame_count( + halide_buffer_t*, + halide_buffer_t*, int32_t num_sensor, bool frame_sync, bool realtime_display_mode, - halide_buffer_t * id_buf, halide_buffer_t* out) -{ const std::string id(reinterpret_cast(id_buf->host)); + halide_buffer_t* id_buf, halide_buffer_t* out) +{ + const std::string id(reinterpret_cast(id_buf->host)); return u3v_camera_frame_count(id, num_sensor, frame_sync, realtime_display_mode, out); } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_camera2_frame_count); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_gendc_camera1( - halide_buffer_t * id_buf, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_gendc_camera1( + halide_buffer_t* id_buf, bool force_sim_mode, int32_t width, int32_t height, float_t fps, bool frame_sync, bool realtime_display_mode, bool enable_control, - halide_buffer_t * gain_key_buf, halide_buffer_t * exposure_key_buf, halide_buffer_t * pixel_format_buf, + halide_buffer_t* gain_key_buf, halide_buffer_t* exposure_key_buf, halide_buffer_t* pixel_format_buf, double gain0, double exposure0, - halide_buffer_t * out_gendc - ) + halide_buffer_t* out_gendc) { using namespace Halide; int num_output = 1; try { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); const std::string gain_key(reinterpret_cast(gain_key_buf->host)); const std::string exposure_key(reinterpret_cast(exposure_key_buf->host)); - const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); + const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); if (out_gendc->is_bounds_query()) { return 0; } - auto &u3v(ion::bb::image_io::U3VGenDC::get_instance(id, 1, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); - // set gain & exposure - if (enable_control){ + auto& u3v(ion::bb::image_io::U3VGenDC::get_instance(id, 1, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); + // set gain & exposure + if (enable_control) { ion::log::debug("Setting gain0:{} exposure0:{}", gain0, exposure0); u3v.set_gain(0, gain_key, gain0); u3v.set_exposure(0, exposure_key, exposure0); } - std::vector obufs{out_gendc->host}; + std::vector obufs { out_gendc->host }; u3v.get(obufs); return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -1558,32 +1564,30 @@ int ION_EXPORT ion_bb_image_io_u3v_gendc_camera1( } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_gendc_camera1); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_gendc_camera2( - halide_buffer_t * id_buf, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_gendc_camera2( + halide_buffer_t* id_buf, bool force_sim_mode, int32_t width, int32_t height, float_t fps, bool frame_sync, bool realtime_display_mode, bool enable_control, - halide_buffer_t * gain_key_buf, halide_buffer_t * exposure_key_buf, halide_buffer_t * pixel_format_buf, + halide_buffer_t* gain_key_buf, halide_buffer_t* exposure_key_buf, halide_buffer_t* pixel_format_buf, double gain0, double exposure0, double gain1, double exposure1, - halide_buffer_t * out_gendc0, halide_buffer_t * out_gendc1 - ) + halide_buffer_t* out_gendc0, halide_buffer_t* out_gendc1) { using namespace Halide; try { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); const std::string gain_key(reinterpret_cast(gain_key_buf->host)); const std::string exposure_key(reinterpret_cast(exposure_key_buf->host)); - const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); - if (out_gendc0->is_bounds_query() || out_gendc1->is_bounds_query() ) { + const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); + if (out_gendc0->is_bounds_query() || out_gendc1->is_bounds_query()) { return 0; } - std::vector obufs{out_gendc0->host, out_gendc1->host}; + std::vector obufs { out_gendc0->host, out_gendc1->host }; - auto &u3v(ion::bb::image_io::U3VGenDC::get_instance(id, 2, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); - // set gain & exposure + auto& u3v(ion::bb::image_io::U3VGenDC::get_instance(id, 2, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); + // set gain & exposure if (enable_control) { ion::log::debug("Setting gain0:{} exposure0:{}", gain0, exposure0); u3v.set_gain(0, gain_key, gain0); @@ -1593,10 +1597,10 @@ int ION_EXPORT ion_bb_image_io_u3v_gendc_camera2( u3v.set_gain(1, gain_key, gain1); u3v.set_exposure(1, exposure_key, exposure1); } - u3v.get(obufs); + u3v.get(obufs); return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -1606,43 +1610,42 @@ int ION_EXPORT ion_bb_image_io_u3v_gendc_camera2( } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_gendc_camera2); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_multiple_camera1( - halide_buffer_t * id_buf, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_multiple_camera1( + halide_buffer_t* id_buf, bool force_sim_mode, int32_t width, int32_t height, float_t fps, bool frame_sync, bool realtime_display_mode, bool enable_control, - halide_buffer_t * gain_key_buf, halide_buffer_t * exposure_key_buf, halide_buffer_t * pixel_format_buf, + halide_buffer_t* gain_key_buf, halide_buffer_t* exposure_key_buf, halide_buffer_t* pixel_format_buf, double gain0, double exposure0, - halide_buffer_t * out0) + halide_buffer_t* out0) { using namespace Halide; int num_output = 1; try { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); const std::string gain_key(reinterpret_cast(gain_key_buf->host)); const std::string exposure_key(reinterpret_cast(exposure_key_buf->host)); - const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); - std::vector> obufs{Halide::Buffer<>(*out0)}; + const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); + std::vector> obufs { Halide::Buffer<>(*out0) }; if (out0->is_bounds_query()) { return 0; } - if(force_sim_mode){ - auto &u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, num_output, width, height, fps, pixel_format)); - u3v.get(obufs); - }else{ - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, num_output, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); - if (enable_control) { - // set gain & exposure + if (force_sim_mode) { + auto& u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, num_output, width, height, fps, pixel_format)); + u3v.get(obufs); + } else { + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, num_output, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); + if (enable_control) { + // set gain & exposure ion::log::debug("Setting gain0:{} exposure0:{}", gain0, exposure0); u3v.set_gain(0, gain_key, gain0); u3v.set_exposure(0, exposure_key, exposure0); - } - u3v.get(obufs); + } + u3v.get(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -1652,35 +1655,34 @@ int ION_EXPORT ion_bb_image_io_u3v_multiple_camera1( } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_multiple_camera1); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_multiple_camera2( - halide_buffer_t * id_buf, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_multiple_camera2( + halide_buffer_t* id_buf, bool force_sim_mode, int32_t width, int32_t height, float_t fps, bool frame_sync, bool realtime_display_mode, bool enable_control, - halide_buffer_t * gain_key_buf, halide_buffer_t * exposure_key_buf, halide_buffer_t * pixel_format_buf, + halide_buffer_t* gain_key_buf, halide_buffer_t* exposure_key_buf, halide_buffer_t* pixel_format_buf, double gain0, double exposure0, double gain1, double exposure1, - halide_buffer_t * out0, halide_buffer_t * out1) + halide_buffer_t* out0, halide_buffer_t* out1) { using namespace Halide; int num_output = 2; try { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); const std::string gain_key(reinterpret_cast(gain_key_buf->host)); const std::string exposure_key(reinterpret_cast(exposure_key_buf->host)); - std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); - std::vector> obufs{Halide::Buffer<>(*out0), Halide::Buffer<>(*out1)}; + std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); + std::vector> obufs { Halide::Buffer<>(*out0), Halide::Buffer<>(*out1) }; if (out0->is_bounds_query() || out1->is_bounds_query()) { return 0; } - if(force_sim_mode){ - auto &u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, num_output, width, height, fps, pixel_format)); - u3v.get(obufs); - }else{ - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, num_output, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); - if (enable_control) { - // set gain & exposure + if (force_sim_mode) { + auto& u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, num_output, width, height, fps, pixel_format)); + u3v.get(obufs); + } else { + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, num_output, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); + if (enable_control) { + // set gain & exposure ion::log::debug("Setting gain0:{} exposure0:{}", gain0, exposure0); u3v.set_gain(0, gain_key, gain0); u3v.set_exposure(0, exposure_key, exposure0); @@ -1688,10 +1690,10 @@ int ION_EXPORT ion_bb_image_io_u3v_multiple_camera2( u3v.set_gain(1, gain_key, gain1); u3v.set_exposure(1, exposure_key, exposure1); } - u3v.get(obufs); + u3v.get(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -1701,36 +1703,35 @@ int ION_EXPORT ion_bb_image_io_u3v_multiple_camera2( } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_multiple_camera2); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_multiple_camera_frame_count1( - halide_buffer_t *, - halide_buffer_t * id_buf, int32_t num_sensor, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_multiple_camera_frame_count1( + halide_buffer_t*, + halide_buffer_t* id_buf, int32_t num_sensor, bool force_sim_mode, int32_t width, int32_t height, float_t fps, bool frame_sync, bool realtime_display_mode, - halide_buffer_t * pixel_format_buf, + halide_buffer_t* pixel_format_buf, halide_buffer_t* out) { try { - const std::string id(reinterpret_cast(id_buf->host)); - const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); - std::vector obufs{out->host}; + const std::string id(reinterpret_cast(id_buf->host)); + const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); + std::vector obufs { out->host }; if (out->is_bounds_query()) { out->dim[0].min = 0; out->dim[0].extent = num_sensor; return 0; } - if(force_sim_mode){ - auto &u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, 1, width, height, fps, pixel_format)); - u3v.get_frame_count(obufs); - }else{ - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 1, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); - u3v.get_frame_count(obufs); + if (force_sim_mode) { + auto& u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, 1, width, height, fps, pixel_format)); + u3v.get_frame_count(obufs); + } else { + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 1, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); + u3v.get_frame_count(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown when get frame count: {}", e.what()); return 1; } catch (...) { @@ -1740,77 +1741,72 @@ int ION_EXPORT ion_bb_image_io_u3v_multiple_camera_frame_count1( } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_multiple_camera_frame_count1); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_multiple_camera_frame_count2( - halide_buffer_t *, - halide_buffer_t *, - halide_buffer_t * id_buf, int32_t num_sensor, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_multiple_camera_frame_count2( + halide_buffer_t*, + halide_buffer_t*, + halide_buffer_t* id_buf, int32_t num_sensor, bool force_sim_mode, int32_t width, int32_t height, float_t fps, bool frame_sync, bool realtime_display_mode, - halide_buffer_t * pixel_format_buf, - halide_buffer_t * out0, halide_buffer_t * out1) + halide_buffer_t* pixel_format_buf, + halide_buffer_t* out0, halide_buffer_t* out1) { try { - const std::string id(reinterpret_cast(id_buf->host)); - const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); - std::vector obufs{out0->host, out1->host}; + const std::string id(reinterpret_cast(id_buf->host)); + const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); + std::vector obufs { out0->host, out1->host }; if (out0->is_bounds_query() || out1->is_bounds_query()) { return 0; } - if(force_sim_mode){ - auto &u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, 2, width, height, fps, pixel_format)); + if (force_sim_mode) { + auto& u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, 2, width, height, fps, pixel_format)); u3v.get_frame_count(obufs); - }else{ - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 2, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); + } else { + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 2, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); u3v.get_frame_count(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown when get frame count: {}", e.what()); return 1; } catch (...) { ion::log::error("Unknown exception was thrown"); return 1; } - } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_multiple_camera_frame_count2); - -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_device_info1( - halide_buffer_t *, - halide_buffer_t * id_buf, int32_t num_sensor, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_device_info1( + halide_buffer_t*, + halide_buffer_t* id_buf, int32_t num_sensor, bool force_sim_mode, int32_t width, int32_t height, float_t fps, bool frame_sync, bool realtime_display_mode, - halide_buffer_t * pixel_format_buf, - halide_buffer_t * out_deviceinfo - ) + halide_buffer_t* pixel_format_buf, + halide_buffer_t* out_deviceinfo) { using namespace Halide; int num_output = 1; try { - const std::string id(reinterpret_cast(id_buf->host)); - const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); + const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); - if (out_deviceinfo->is_bounds_query()){ + if (out_deviceinfo->is_bounds_query()) { out_deviceinfo->dim[0].min = 0; out_deviceinfo->dim[0].extent = sizeof(ion::bb::image_io::rawHeader); return 0; } - std::vector obufs{out_deviceinfo->host}; - if(force_sim_mode){ - auto &u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, 1, width, height, fps, pixel_format)); + std::vector obufs { out_deviceinfo->host }; + if (force_sim_mode) { + auto& u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, 1, width, height, fps, pixel_format)); u3v.get_device_info(obufs); - }else{ - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 1, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); + } else { + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 1, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); u3v.get_device_info(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -1820,45 +1816,43 @@ int ION_EXPORT ion_bb_image_io_u3v_device_info1( } ION_REGISTER_EXTERN(ion_bb_image_io_u3v_device_info1); -extern "C" -int ION_EXPORT ion_bb_image_io_u3v_device_info2( - halide_buffer_t *, halide_buffer_t *, - halide_buffer_t * id_buf, int32_t num_sensor, +extern "C" int ION_EXPORT ion_bb_image_io_u3v_device_info2( + halide_buffer_t*, halide_buffer_t*, + halide_buffer_t* id_buf, int32_t num_sensor, bool force_sim_mode, int32_t width, int32_t height, float_t fps, bool frame_sync, bool realtime_display_mode, - halide_buffer_t * pixel_format_buf, - halide_buffer_t * deviceinfo0, halide_buffer_t * deviceinfo1 - ) + halide_buffer_t* pixel_format_buf, + halide_buffer_t* deviceinfo0, halide_buffer_t* deviceinfo1) { using namespace Halide; try { - const std::string id(reinterpret_cast(id_buf->host)); + const std::string id(reinterpret_cast(id_buf->host)); int num_output = 2; - const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); + const std::string pixel_format(reinterpret_cast(pixel_format_buf->host)); if (deviceinfo0->is_bounds_query() || deviceinfo1->is_bounds_query()) { - if (deviceinfo0->is_bounds_query()){ + if (deviceinfo0->is_bounds_query()) { deviceinfo0->dim[0].min = 0; deviceinfo0->dim[0].extent = sizeof(ion::bb::image_io::rawHeader); } - if (deviceinfo1->is_bounds_query()){ + if (deviceinfo1->is_bounds_query()) { deviceinfo1->dim[0].min = 0; deviceinfo1->dim[0].extent = sizeof(ion::bb::image_io::rawHeader); } return 0; } - std::vector obufs{deviceinfo0->host, deviceinfo1->host}; - if(force_sim_mode){ - auto &u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, 2, width, height, fps, pixel_format)); + std::vector obufs { deviceinfo0->host, deviceinfo1->host }; + if (force_sim_mode) { + auto& u3v(ion::bb::image_io::U3VFakeCam::get_instance(id, 2, width, height, fps, pixel_format)); u3v.get_device_info(obufs); - }else{ - auto &u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 2, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); + } else { + auto& u3v(ion::bb::image_io::U3VRealCam::get_instance(id, 2, frame_sync, realtime_display_mode, force_sim_mode, width, height, fps, pixel_format)); u3v.get_device_info(obufs); } return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { diff --git a/src/bb/image-io/rt_v4l2.h b/src/bb/image-io/rt_v4l2.h index 34116359..9bf9772f 100644 --- a/src/bb/image-io/rt_v4l2.h +++ b/src/bb/image-io/rt_v4l2.h @@ -1,8 +1,8 @@ #ifndef ION_BB_IMAGE_IO_RT_V4L2_H #define ION_BB_IMAGE_IO_RT_V4L2_H -#include #include +#include #include #include #include @@ -19,654 +19,690 @@ #include #include -#include #include "halide_image_io.h" +#include #include "log.h" -#include "rt_common.h" #include "httplib.h" +#include "rt_common.h" namespace ion { namespace bb { -namespace image_io { + namespace image_io { #define BORDER_INTERPOLATE(x, l) (x < 0 ? 0 : (x >= l ? l - 1 : x)) -float weight(float input){ + float weight(float input) + { float alpha = -1; - float x = (input < 0)? -input : input; + float x = (input < 0) ? -input : input; float x2 = x * x; float x3 = x * x * x; - if(x <= 1){ + if (x <= 1) { return (alpha + 2) * x3 - (alpha + 3) * x2 + 1; - }else if(x < 2){ + } else if (x < 2) { return alpha * x3 - 5 * alpha * x2 + 8 * alpha * x - 4 * alpha; - }else{ + } else { return 0x0; } } -template -void resize_bicubic(Halide::Runtime::Buffer& dst, - const Halide::Runtime::Buffer& src, - const int32_t src_width, const int32_t src_height, - const uint32_t dst_width, const uint32_t dst_height){ - double min_value = static_cast(std::numeric_limits::min()); - double max_value = static_cast(std::numeric_limits::max()); - for(int c = 0; c < 3; c++){ - for(int dh = 0; dh < dst_height; dh++){ - for(int dw = 0; dw < dst_width; dw++){ - double value = 0; - float totalWeight = 0; - - float x = ((static_cast(dw)+ 0.5f) - *static_cast(src_width)) / static_cast(dst_width); - x -= 0.5f; - float y = (static_cast(dh)+ 0.5f) - *static_cast(src_height) / static_cast(dst_height); - y -= 0.5f; - float dx = x - static_cast(floor(x)); - float dy = y - static_cast(floor(y)); - - for(int i = -1; i < 3; i++){ - for(int j = -1; j < 3; j++){ - - float wx = weight(j - dx); - float wy = weight(i - dy); - float w = wx * wy; - - int sw = BORDER_INTERPOLATE((int)(x + j), src_width); - int sh = BORDER_INTERPOLATE((int)(y + i), src_height); - T s = src(sw, sh, c); - - value += w*s; - totalWeight += w; + template + void resize_bicubic(Halide::Runtime::Buffer& dst, + const Halide::Runtime::Buffer& src, + const int32_t src_width, const int32_t src_height, + const uint32_t dst_width, const uint32_t dst_height) + { + double min_value = static_cast(std::numeric_limits::min()); + double max_value = static_cast(std::numeric_limits::max()); + for (int c = 0; c < 3; c++) { + for (int dh = 0; dh < dst_height; dh++) { + for (int dw = 0; dw < dst_width; dw++) { + double value = 0; + float totalWeight = 0; + + float x = ((static_cast(dw) + 0.5f) + * static_cast(src_width)) + / static_cast(dst_width); + x -= 0.5f; + float y = (static_cast(dh) + 0.5f) + * static_cast(src_height) / static_cast(dst_height); + y -= 0.5f; + float dx = x - static_cast(floor(x)); + float dy = y - static_cast(floor(y)); + + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { + + float wx = weight(j - dx); + float wy = weight(i - dy); + float w = wx * wy; + + int sw = BORDER_INTERPOLATE((int)(x + j), src_width); + int sh = BORDER_INTERPOLATE((int)(y + i), src_height); + T s = src(sw, sh, c); + + value += w * s; + totalWeight += w; + } + } + if (fabs(totalWeight) > 0) { + value /= fabs(totalWeight); + } else { + value = 0; + } + value += 0.5; + value = (value < min_value) ? min_value : value; + value = (value > max_value) ? max_value : value; + dst(dw, dh, c) = static_cast(value); } - } - if(fabs(totalWeight)>0){ - value /= fabs(totalWeight); - }else{ - value= 0; - } - value += 0.5; - value = (value < min_value) ? min_value : value; - value = (value > max_value) ? max_value : value; - dst(dw, dh, c) = static_cast(value); - } + } } - } -} + std::unordered_map> image_cache; -std::unordered_map> image_cache; + template + bool get_image(const std::string& url, Halide::Runtime::Buffer& img, int width_, int height_) + { + bool img_loaded = false; + if (url.empty()) { + return img_loaded; + } -template -bool get_image(const std::string &url, Halide::Runtime::Buffer &img, int width_, int height_) { - bool img_loaded = false; - if (url.empty()) { - return img_loaded; - } + std::string host_name; + std::string path_name; + std::tie(host_name, path_name) = parse_url(url); - std::string host_name; - std::string path_name; - std::tie(host_name, path_name) = parse_url(url); + Halide::Runtime::Buffer img_buf; + if (host_name.empty() || path_name.empty()) { + // fallback to local file + if (std::filesystem::exists(url)) { + img_buf = Halide::Tools::load_and_convert_image(url); + img_loaded = true; + } + } else { + httplib::Client cli(host_name.c_str()); + cli.set_follow_location(true); + auto res = cli.Get(path_name.c_str()); + if (res && res->status == 200) { + std::vector data(res->body.size()); + data.resize(res->body.size()); + std::memcpy(data.data(), res->body.c_str(), res->body.size()); + std::filesystem::path dir_path = std::filesystem::temp_directory_path() / "simulation_camera"; + ; + if (!std::filesystem::exists(dir_path)) { + if (!std::filesystem::create_directory(dir_path)) { + throw std::runtime_error("Failed to create temporary directory"); + } + } + std::ofstream ofs(dir_path / std::filesystem::path(url).filename(), std::ios::binary); + ofs.write(reinterpret_cast(data.data()), data.size()); - Halide::Runtime::Buffer img_buf; - if (host_name.empty() || path_name.empty()){ - // fallback to local file - if (std::filesystem::exists(url)){ - img_buf = Halide::Tools::load_and_convert_image(url); - img_loaded = true; - } - }else{ - httplib::Client cli(host_name.c_str()); - cli.set_follow_location(true); - auto res = cli.Get(path_name.c_str()); - if (res && res->status == 200) { - std::vector data(res->body.size()); - data.resize(res->body.size()); - std::memcpy(data.data(), res->body.c_str(), res->body.size()); - std::filesystem::path dir_path = std::filesystem::temp_directory_path() / "simulation_camera";; - if (!std::filesystem::exists(dir_path)) { - if (!std::filesystem::create_directory(dir_path)) { - throw std::runtime_error("Failed to create temporary directory"); + img_buf = Halide::Tools::load_and_convert_image(dir_path / std::filesystem::path(url).filename()); + img_loaded = true; } } - std::ofstream ofs(dir_path / std::filesystem::path(url).filename(), std::ios::binary); - ofs.write(reinterpret_cast(data.data()), data.size()); - - img_buf = Halide::Tools::load_and_convert_image(dir_path / std::filesystem::path(url).filename()); - img_loaded = true; - - } - } - if (img_loaded){ //resize - int ori_width = img_buf.width(); - int ori_height = img_buf.height(); - int channels = img_buf.channels(); - Halide::Runtime::Buffer resized (width_, height_, 3); - resize_bicubic(resized, img_buf, ori_width, ori_height, width_, height_); - - if (sizeof(T) == 4){ //float - // Buffer to Buffer range(0-1) - Halide::Runtime::Buffer float_img = Halide::Tools::ImageTypeConversion::convert_image(resized, halide_type_of()); - img.copy_from(float_img); - }else if (sizeof(T) == 1){ //uint_8 - img.copy_from(resized); - }else{ - throw std::runtime_error("Unsupported image format"); + if (img_loaded) { // resize + int ori_width = img_buf.width(); + int ori_height = img_buf.height(); + int channels = img_buf.channels(); + Halide::Runtime::Buffer resized(width_, height_, 3); + resize_bicubic(resized, img_buf, ori_width, ori_height, width_, height_); + + if (sizeof(T) == 4) { // float + // Buffer to Buffer range(0-1) + Halide::Runtime::Buffer float_img = Halide::Tools::ImageTypeConversion::convert_image(resized, halide_type_of()); + img.copy_from(float_img); + } else if (sizeof(T) == 1) { // uint_8 + img.copy_from(resized); + } else { + throw std::runtime_error("Unsupported image format"); + } + } + return img_loaded; } - } - return img_loaded; -} -} // namespace image_io -} // namespace bb -} // namespace ion + } // namespace image_io +} // namespace bb +} // namespace ion namespace ion { namespace bb { -namespace image_io { - -int xioctl(int fd, int request, void *arg) { - int r; - do { - r = ioctl(fd, request, arg); - } while (-1 == r && EINTR == errno); - return r; -} + namespace image_io { + + int xioctl(int fd, int request, void* arg) + { + int r; + do { + r = ioctl(fd, request, arg); + } while (-1 == r && EINTR == errno); + return r; + } + + class V4L2 { + + struct Buffer { + void* start; + size_t length; + }; + + public: + static V4L2& get_instance(int32_t id, int32_t index, int32_t fps, int32_t width, int32_t height, uint32_t pixel_format, + float gain_r, float gain_g, float gain_b, float offset, int32_t bit_width, int32_t bit_shift, + bool force_sim_mode, const std::string& url) + { + if (instances_.count(id) == 0) { + instances_[id] = std::make_shared(id, index, fps, width, height, pixel_format, gain_r, gain_g, gain_b, offset, bit_width, bit_shift, force_sim_mode, url); + } + return *instances_[id]; + } -class V4L2 { + V4L2(int32_t id, int32_t index, int32_t fps, int32_t width, int32_t height, uint32_t pixel_format, + float gain_r, float gain_g, float gain_b, float offset, int32_t bit_width, int32_t bit_shift, + bool force_sim_mode, const std::string& url) + : id_(id) + , index_(index) + , fps_(fps) + , width_(width) + , height_(height) + , pixel_format_(pixel_format) + , gain_r_(gain_r) + , gain_g_(gain_g) + , gain_b_(gain_b) + , offset_(offset) + , bit_width_(bit_width) + , bit_shift_(bit_shift) + , sim_mode_(force_sim_mode) + , url_(url) + { + + using namespace std; + + // + // Initialize device + // + std::string dev_name_str = "/dev/video" + std::to_string(index); + const char* dev_name = dev_name_str.c_str(); + struct stat st; + if (-1 == stat(dev_name, &st)) { + log::warn("Fallback to simulation mode: Could not find {}", dev_name); + sim_mode_ = true; + ; + return; + } + if (!S_ISCHR(st.st_mode)) { + log::warn("Fallback to simulation mode: {} is not proper device", dev_name); + sim_mode_ = true; + ; + return; + } - struct Buffer { - void *start; - size_t length; - }; + fd_ = open(dev_name, O_RDWR | O_NONBLOCK, 0); + if (-1 == fd_) { + log::warn("Fallback to simulation mode: Cannot open {}: {}, {}", dev_name, errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } -public: + struct v4l2_capability cap; + if (-1 == xioctl(fd_, VIDIOC_QUERYCAP, &cap)) { + if (EINVAL == errno) { + log::warn("Fallback to simulation mode: {} is not V4L2 device", dev_name); + sim_mode_ = true; + ; + return; + } else { + log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_QUERYCAP", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } + } + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + log::warn("Fallback to simulation mode: {} is not video capture device", dev_name); + sim_mode_ = true; + ; + return; + } + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + log::warn("Fallback to simulation mode: {} s does not support streaming i/o", dev_name); + sim_mode_ = true; + ; + return; + } - static V4L2 &get_instance(int32_t id, int32_t index, int32_t fps, int32_t width, int32_t height, uint32_t pixel_format, - float gain_r, float gain_g, float gain_b, float offset, int32_t bit_width, int32_t bit_shift, - bool force_sim_mode, const std::string& url) { - if (instances_.count(id) == 0) { - instances_[id] = std::make_shared(id, index, fps, width, height, pixel_format, gain_r, gain_g, gain_b, offset, bit_width, bit_shift, force_sim_mode, url); - } - return *instances_[id]; - } + uint32_t desired_pixel_format = pixel_format; - V4L2(int32_t id, int32_t index, int32_t fps, int32_t width, int32_t height, uint32_t pixel_format, - float gain_r, float gain_g, float gain_b, float offset, int32_t bit_width, int32_t bit_shift, - bool force_sim_mode, const std::string& url) - : id_(id), index_(index), fps_(fps), width_(width), height_(height), pixel_format_(pixel_format), - gain_r_(gain_r), gain_g_(gain_g), gain_b_(gain_b), offset_(offset), bit_width_(bit_width), bit_shift_(bit_shift), - sim_mode_(force_sim_mode), url_(url) { - - using namespace std; - - // - // Initialize device - // - std::string dev_name_str = "/dev/video" + std::to_string(index); - const char *dev_name = dev_name_str.c_str(); - struct stat st; - if (-1 == stat(dev_name, &st)) { - log::warn("Fallback to simulation mode: Could not find {}", dev_name); - sim_mode_ = true;; - return; - } - if (!S_ISCHR(st.st_mode)) { - log::warn("Fallback to simulation mode: {} is not proper device", dev_name); - sim_mode_ = true;; - return; - } + struct v4l2_fmtdesc fmtdesc; + memset(&fmtdesc, 0, sizeof(fmtdesc)); + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fd_ = open(dev_name, O_RDWR | O_NONBLOCK, 0); - if (-1 == fd_) { - log::warn("Fallback to simulation mode: Cannot open {}: {}, {}", dev_name, errno, strerror(errno)); - sim_mode_ = true;; - return; - } + bool supported = false; + while (0 == xioctl(fd_, VIDIOC_ENUM_FMT, &fmtdesc)) { + if (fmtdesc.pixelformat == desired_pixel_format) { + supported = true; + } + fmtdesc.index++; + } + if (!supported) { + log::warn("Fallback to simulation mode: {} does not support desired pixel format", dev_name); + sim_mode_ = true; + ; + return; + } - struct v4l2_capability cap; - if (-1 == xioctl(fd_, VIDIOC_QUERYCAP, &cap)) { - if (EINVAL == errno) { - log::warn("Fallback to simulation mode: {} is not V4L2 device", dev_name); - sim_mode_ = true;; - return; - } else { - log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_QUERYCAP", errno, strerror(errno)); - sim_mode_ = true;; - return; - } - } - if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { - log::warn("Fallback to simulation mode: {} is not video capture device", dev_name); - sim_mode_ = true;; - return; - } - if (!(cap.capabilities & V4L2_CAP_STREAMING)) { - log::warn("Fallback to simulation mode: {} s does not support streaming i/o", dev_name); - sim_mode_ = true;; - return; - } + struct v4l2_format fmt { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt = { + .pix = { + .width = static_cast<__u32>(width), + .height = static_cast<__u32>(height), + .pixelformat = pixel_format, + .field = V4L2_FIELD_INTERLACED, + } + }, + }; + if (-1 == xioctl(fd_, VIDIOC_S_FMT, &fmt)) { + log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_S_FMT", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } + if (width != fmt.fmt.pix.width || height != fmt.fmt.pix.height) { + log::warn("Fallback to simulation mode: {} does not support desired resolution, expected({}x{}), actual({}x{})", dev_name, fmt.fmt.pix.width, fmt.fmt.pix.height, width, height); + sim_mode_ = true; + ; + return; + } + buffer_size_ = fmt.fmt.pix.sizeimage; + + struct v4l2_streamparm strmp { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + if (-1 == xioctl(fd_, VIDIOC_G_PARM, &strmp)) { + log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_G_PARM", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } + strmp.parm.capture.timeperframe.numerator = 1; + strmp.parm.capture.timeperframe.denominator = fps; + if (-1 == xioctl(fd_, VIDIOC_S_PARM, &strmp)) { + log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_S_PARM", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } - uint32_t desired_pixel_format = pixel_format; + // + // Initialize mapped memory + // + struct v4l2_requestbuffers req; + req.count = 2; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_USERPTR; + + if (-1 == xioctl(fd_, VIDIOC_REQBUFS, &req)) { + if (EINVAL == errno) { + log::warn("Fallback to simulation mode: {} does not support memory mapping", dev_name); + sim_mode_ = true; + ; + return; + } else { + log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_REQBUFS", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } + } - struct v4l2_fmtdesc fmtdesc; - memset(&fmtdesc, 0, sizeof(fmtdesc)); - fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + const size_t align_unit = 64; + const size_t size = (fmt.fmt.pix.sizeimage + align_unit - 1) / align_unit * align_unit; + for (int i = 0; i < req.count; ++i) { + Buffer buffer; + buffer.start = aligned_alloc(align_unit, fmt.fmt.pix.sizeimage); + buffer.length = fmt.fmt.pix.sizeimage; - bool supported = false; - while (0 == xioctl(fd_, VIDIOC_ENUM_FMT, &fmtdesc)) { - if (fmtdesc.pixelformat == desired_pixel_format) { - supported = true; - } - fmtdesc.index++; - } - if (!supported) { - log::warn("Fallback to simulation mode: {} does not support desired pixel format", dev_name); - sim_mode_ = true;; - return; - } + buffers_.push_back(buffer); + } - struct v4l2_format fmt { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt = { - .pix = { - .width = static_cast<__u32>(width), - .height = static_cast<__u32>(height), - .pixelformat = pixel_format, - .field = V4L2_FIELD_INTERLACED, - }}, - }; - if (-1 == xioctl(fd_, VIDIOC_S_FMT, &fmt)) { - log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_S_FMT", errno, strerror(errno)); - sim_mode_ = true;; - return; - } - if (width != fmt.fmt.pix.width || height != fmt.fmt.pix.height) { - log::warn("Fallback to simulation mode: {} does not support desired resolution, expected({}x{}), actual({}x{})", dev_name, fmt.fmt.pix.width, fmt.fmt.pix.height, width, height); - sim_mode_ = true;; - return; - } - buffer_size_ = fmt.fmt.pix.sizeimage; + // + // Start capture + // + for (int i = 0; i < buffers_.size() - 1; ++i) { + struct v4l2_buffer buf; + buf.index = static_cast<__u32>(i); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + buf.m.userptr = reinterpret_cast(buffers_[i].start); + buf.length = buffers_[i].length; + + /* enqueue an empty (capturing) or filled (output) buffer in the driver's incoming queue */ + if (-1 == xioctl(fd_, VIDIOC_QBUF, &buf)) { + log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_QBUF", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } + } + next_buffer_.index = static_cast<__u32>(buffers_.size() - 1); + next_buffer_.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + next_buffer_.memory = V4L2_MEMORY_USERPTR; + next_buffer_.m.userptr = reinterpret_cast(buffers_[buffers_.size() - 1].start); + next_buffer_.length = buffers_[buffers_.size() - 1].length; + + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + /* Start streaming I/O */ + if (-1 == xioctl(fd_, VIDIOC_STREAMON, &type)) { + log::warn("Fallback to simulation mode: {} error {}, {}\n", "VIDIOC_STREAMON", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } - struct v4l2_streamparm strmp { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + // + // Initialize event + // + efd_ = epoll_create1(0); + if (-1 == efd_) { + log::warn("Fallback to simulation mode: {} error {}, {}", "epoll_create1", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } - }; - if (-1 == xioctl(fd_, VIDIOC_G_PARM, &strmp)) { - log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_G_PARM", errno, strerror(errno)); - sim_mode_ = true;; - return; - } - strmp.parm.capture.timeperframe.numerator = 1; - strmp.parm.capture.timeperframe.denominator = fps; - if (-1 == xioctl(fd_, VIDIOC_S_PARM, &strmp)) { - log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_S_PARM", errno, strerror(errno)); - sim_mode_ = true;; - return; - } + struct epoll_event event; + event.events = EPOLLIN | EPOLLET; + event.data.fd = fd_; - // - // Initialize mapped memory - // - struct v4l2_requestbuffers req; - req.count = 2; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_USERPTR; - - if (-1 == xioctl(fd_, VIDIOC_REQBUFS, &req)) { - if (EINVAL == errno) { - log::warn("Fallback to simulation mode: {} does not support memory mapping", dev_name); - sim_mode_ = true;; - return; - } else { - log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_REQBUFS", errno, strerror(errno)); - sim_mode_ = true;; - return; + if (-1 == epoll_ctl(efd_, EPOLL_CTL_ADD, fd_, &event)) { + log::warn("Fallback to simulation mode: {} error {}, {}", "epoll_ctl", errno, strerror(errno)); + sim_mode_ = true; + ; + return; + } } - } - - const size_t align_unit = 64; - const size_t size = (fmt.fmt.pix.sizeimage + align_unit - 1) / align_unit * align_unit; - for (int i = 0; i < req.count; ++i) { - Buffer buffer; - buffer.start = aligned_alloc(align_unit, fmt.fmt.pix.sizeimage); - buffer.length = fmt.fmt.pix.sizeimage; - buffers_.push_back(buffer); - } + // 0: RGGB, 1:BGGR, 2: GRBG, 3:GBRG + int bayer_pattern(uint32_t pixel_format) + { + switch (pixel_format) { + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SRGGB12: + return 0; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SBGGR12: + return 1; + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SGRBG12: + return 2; + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGBRG12: + return 3; + deafult: + throw std::runtime_error("Unreachable"); + } - // - // Start capture - // - for (int i = 0; i < buffers_.size() - 1; ++i) { - struct v4l2_buffer buf; - buf.index = static_cast<__u32>(i); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_USERPTR; - buf.m.userptr = reinterpret_cast(buffers_[i].start); - buf.length = buffers_[i].length; - - /* enqueue an empty (capturing) or filled (output) buffer in the driver's incoming queue */ - if (-1 == xioctl(fd_, VIDIOC_QBUF, &buf)) { - log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_QBUF", errno, strerror(errno)); - sim_mode_ = true;; - return; + return -1; } - } - next_buffer_.index = static_cast<__u32>(buffers_.size() - 1); - next_buffer_.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - next_buffer_.memory = V4L2_MEMORY_USERPTR; - next_buffer_.m.userptr = reinterpret_cast(buffers_[buffers_.size() - 1].start); - next_buffer_.length = buffers_[buffers_.size() - 1].length; - - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - /* Start streaming I/O */ - if (-1 == xioctl(fd_, VIDIOC_STREAMON, &type)) { - log::warn("Fallback to simulation mode: {} error {}, {}\n", "VIDIOC_STREAMON", errno, strerror(errno)); - sim_mode_ = true;; - return; - } - - // - // Initialize event - // - efd_ = epoll_create1(0); - if (-1 == efd_) { - log::warn("Fallback to simulation mode: {} error {}, {}", "epoll_create1", errno, strerror(errno)); - sim_mode_ = true;; - return; - } - - struct epoll_event event; - event.events = EPOLLIN | EPOLLET; - event.data.fd = fd_; - - if (-1 == epoll_ctl(efd_, EPOLL_CTL_ADD, fd_, &event)) { - log::warn("Fallback to simulation mode: {} error {}, {}", "epoll_ctl", errno, strerror(errno)); - sim_mode_ = true;; - return; - } - } - - // 0: RGGB, 1:BGGR, 2: GRBG, 3:GBRG - int bayer_pattern(uint32_t pixel_format) { - switch (pixel_format) { - case V4L2_PIX_FMT_SRGGB8: - case V4L2_PIX_FMT_SRGGB10: - case V4L2_PIX_FMT_SRGGB12: - return 0; - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SBGGR12: - return 1; - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SGRBG12: - return 2; - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGBRG12: - return 3; - deafult: - throw std::runtime_error("Unreachable"); - } - return -1; - } + template + void generate_bayer(Halide::Runtime::Buffer& buf) + { + auto it = ion::bb::image_io::image_cache.find(id_); + if (it != ion::bb::image_io::image_cache.end()) { + memcpy(buf.data(), it->second.data(), it->second.size()); + return; + } - template - void generate_bayer(Halide::Runtime::Buffer &buf) { - auto it = ion::bb::image_io::image_cache.find(id_); - if (it != ion::bb::image_io::image_cache.end()) { - memcpy(buf.data(), it->second.data(), it->second.size()); - return; - } + Halide::Runtime::Buffer img(width_, height_, 3); + bool is_loaded = get_image(url_, img, width_, height_); + + if (!is_loaded) { + // Fill by dummy image + Halide::Runtime::Buffer img_16(width_, height_); + img_16.fill(0); + for (int y = (index_ / 2) % 2; y < height_; y += 2) { + for (int x = index_ % 2; x < width_; x += 2) { + img_16(x, y) = 65535; + } + } + buf.copy_from(img_16); + auto size = width_ * height_ * sizeof(uint16_t); + std::vector data(size); + memcpy(data.data(), img_16.data(), size); + ion::bb::image_io::image_cache[id_] = data; + return; + } - Halide::Runtime::Buffer img(width_, height_, 3); - bool is_loaded = get_image(url_, img, width_, height_); - - if (!is_loaded) { - // Fill by dummy image - Halide::Runtime::Buffer img_16(width_, height_); - img_16.fill(0); - for(int y = (index_ / 2) % 2; y < height_ ; y+=2){ - for(int x = index_ % 2; x < width_; x+=2){ - img_16(x, y) = 65535 ; - } - } - buf.copy_from(img_16); - auto size = width_ * height_ * sizeof(uint16_t); - std::vector data(size); - memcpy(data.data(), img_16.data(), size); - ion::bb::image_io::image_cache[id_] = data; - return; - } + img.for_each_element([&](int x, int y, int c) { + img(x, y, c) = pow((float)(img(x, y, c)), 2.2); + }); + + std::vector r_planar(width_ * height_); + std::vector g_planar(width_ * height_); + std::vector b_planar(width_ * height_); + + memcpy(r_planar.data(), img.data(), width_ * height_ * sizeof(float)); + memcpy(g_planar.data(), img.data() + width_ * height_ * 1, width_ * height_ * sizeof(float)); + memcpy(b_planar.data(), img.data() + width_ * height_ * 2, width_ * height_ * sizeof(float)); + + std::transform(r_planar.begin(), r_planar.end(), r_planar.begin(), [&](float x) { return std::min(std::max(x * gain_r_ + offset_, 0.f), 1.f); }); + std::transform(g_planar.begin(), g_planar.end(), g_planar.begin(), [&](float x) { return std::min(std::max(x * gain_g_ + offset_, 0.f), 1.f); }); + std::transform(b_planar.begin(), b_planar.end(), b_planar.begin(), [&](float x) { return std::min(std::max(x * gain_b_ + offset_, 0.f), 1.f); }); + + std::vector processed_img_arr(width_ * height_); + int idx = 0; + for (int j = 0; j < height_; j++) { + int evenRow = j % 2 == 0; + for (int i = 0; i < width_; i++) { + int evenCol = i % 2 == 0; + switch (bayer_pattern(pixel_format_)) { + case 0: // RGGB + processed_img_arr[idx] = evenRow ? (evenCol ? r_planar[idx] : g_planar[idx]) : (evenCol ? g_planar[idx] : b_planar[idx]); + break; + case 1: // BGGR + processed_img_arr[idx] = evenRow ? (evenCol ? b_planar[idx] : g_planar[idx]) : (evenCol ? g_planar[idx] : r_planar[idx]); + break; + case 2: // GRBG + processed_img_arr[idx] = evenRow ? (evenCol ? g_planar[idx] : r_planar[idx]) : (evenCol ? b_planar[idx] : g_planar[idx]); + break; + case 3: // GBRG + processed_img_arr[idx] = evenRow ? (evenCol ? g_planar[idx] : b_planar[idx]) : (evenCol ? r_planar[idx] : g_planar[idx]); + break; + } + idx += 1; + } + } - img.for_each_element([&](int x, int y, int c) { - img(x, y, c) = pow((float)(img(x, y, c)), 2.2) ; - }); - - std::vector r_planar(width_*height_); - std::vector g_planar(width_*height_); - std::vector b_planar(width_*height_); - - memcpy(r_planar.data(), img.data(), width_* height_* sizeof(float)); - memcpy(g_planar.data(), img.data() + width_ * height_ * 1, width_* height_* sizeof(float)); - memcpy(b_planar.data(), img.data() + width_ * height_ * 2, width_* height_* sizeof(float)); - - std::transform(r_planar.begin(), r_planar.end(), r_planar.begin(), [&](float x){return std::min(std::max(x * gain_r_ + offset_, 0.f), 1.f);}); - std::transform(g_planar.begin(), g_planar.end(), g_planar.begin(), [&](float x){return std::min(std::max(x * gain_g_ + offset_, 0.f), 1.f);}); - std::transform(b_planar.begin(), b_planar.end(), b_planar.begin(), [&](float x){return std::min(std::max(x * gain_b_ + offset_, 0.f), 1.f);}); - - std::vector processed_img_arr(width_*height_); - int idx = 0; - for (int j = 0; j < height_; j++) { - int evenRow = j % 2 == 0; - for (int i = 0; i < width_; i++) { - int evenCol = i % 2 == 0; - switch (bayer_pattern(pixel_format_)) { - case 0: // RGGB - processed_img_arr[idx] = evenRow ? (evenCol ? r_planar[idx] : g_planar[idx]) : (evenCol ? g_planar[idx] : b_planar[idx]); - break; - case 1: // BGGR - processed_img_arr[idx] = evenRow ? (evenCol ? b_planar[idx] : g_planar[idx]) : (evenCol ? g_planar[idx] : r_planar[idx]); - break; - case 2: // GRBG - processed_img_arr[idx] = evenRow ? (evenCol ? g_planar[idx] : r_planar[idx]) : (evenCol ? b_planar[idx] : g_planar[idx]); - break; - case 3: // GBRG - processed_img_arr[idx] = evenRow ? (evenCol ? g_planar[idx] : b_planar[idx]) : (evenCol ? r_planar[idx] : g_planar[idx]); - break; - } - idx+=1; + std::vector bit_shifted_img_arr(width_ * height_); + for (int i = 0; i < width_ * height_; i++) { + float val = processed_img_arr[i]; + val *= (float)((1 << bit_width_) - 1); + val = val * (float)(1 << bit_shift_) + val / (float)(1 << (bit_width_ - bit_shift_)); + bit_shifted_img_arr[i] = static_cast(val); + } + std::vector data(bit_shifted_img_arr.size() * sizeof(uint16_t)); + memcpy(data.data(), bit_shifted_img_arr.data(), bit_shifted_img_arr.size() * sizeof(uint16_t)); + memcpy(buf.data(), bit_shifted_img_arr.data(), bit_shifted_img_arr.size() * sizeof(uint16_t)); + ion::bb::image_io::image_cache[id_] = data; } - } - - std::vector bit_shifted_img_arr(width_*height_); - for (int i = 0;i(val); - } - std::vector data(bit_shifted_img_arr.size() * sizeof(uint16_t)); - memcpy(data.data(), bit_shifted_img_arr.data(), bit_shifted_img_arr.size() * sizeof(uint16_t)); - memcpy(buf.data(), bit_shifted_img_arr.data(), bit_shifted_img_arr.size() * sizeof(uint16_t)); - ion::bb::image_io::image_cache[id_] = data; - } + // Created by : Harris Zhu + // Filename : rgb2I420.cpp + // Avthor : Harris Zhu + //======================================================================= -// Created by : Harris Zhu -// Filename : rgb2I420.cpp -// Avthor : Harris Zhu -//======================================================================= - -#include #include +#include #define BORDER_INTERPOLATE(x, l) (x < 0 ? 0 : (x >= l ? l - 1 : x)) - template - void rgb2YCrCb(T *destination, Halide::Runtime::Buffer &rgb, int width, int height){ - for(int y = 0; y < height ; y++){ - for(int x = 0; x < width; x++){ - T r = rgb(x, y, 0); - T g = rgb(x, y, 1); - T b = rgb(x, y, 2); - - T Yy = 0.299 * r + 0.587 * g + 0.114 * b ; - T Cr = (r-Yy) * 0.713 + 128; - T Cb = (b-Yy) * 0.564 + 128; - destination[(x+y*width)*3] = Yy; - destination[(x+y*width)*3+1] = Cr; - destination[(x+y*width)*3+2] = Cb; - } - } - - } - - template - void generate_yuyv(Halide::Runtime::Buffer &buf) { - auto it = ion::bb::image_io::image_cache.find(id_); - if (it != ion::bb::image_io::image_cache.end()) { - memcpy(buf.data(), it->second.data(), it->second.size()); - return; - } - Halide::Runtime::Buffer img (width_, height_, 3); - bool is_loaded = get_image(url_, img, width_, height_);; - std::vector yuyv_img(2 * width_ * height_); - if (!is_loaded) { - // Fill by dummy image - for (int y = 0; y < height_; ++y) { - for (int x = 0; x < 2 * width_; ++x) { - yuyv_img[2 * width_ * y + x] = (y * 2 * width_ + x) % 255; + template + void rgb2YCrCb(T* destination, Halide::Runtime::Buffer& rgb, int width, int height) + { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + T r = rgb(x, y, 0); + T g = rgb(x, y, 1); + T b = rgb(x, y, 2); + + T Yy = 0.299 * r + 0.587 * g + 0.114 * b; + T Cr = (r - Yy) * 0.713 + 128; + T Cb = (b - Yy) * 0.564 + 128; + destination[(x + y * width) * 3] = Yy; + destination[(x + y * width) * 3 + 1] = Cr; + destination[(x + y * width) * 3 + 2] = Cb; + } } } - } else { - std::vector yuv(3 * width_ * width_); - rgb2YCrCb(yuv.data(), img, width_, height_); - - for (int y = 0; y < height_; ++y) { - for (int x = 0; x < width_; ++x) { - // Y - yuyv_img[2 * width_ * y + 2 * x + 0] = yuv[( x + y * width_) * 3]; - // Cb or Cr - yuyv_img[2 * width_ * y + 2 * x + 1] = ((x % 2) == 1) ? yuv[(y * width_ + x) * 3 + 1]:yuv[(y * width_ + x) * 3 + 2]; + template + void generate_yuyv(Halide::Runtime::Buffer& buf) + { + auto it = ion::bb::image_io::image_cache.find(id_); + if (it != ion::bb::image_io::image_cache.end()) { + memcpy(buf.data(), it->second.data(), it->second.size()); + return; } - } - } - memcpy(buf.data(), yuyv_img.data(), yuyv_img.size()); - ion::bb::image_io::image_cache[id_] = yuyv_img; - - return; - } + Halide::Runtime::Buffer img(width_, height_, 3); + bool is_loaded = get_image(url_, img, width_, height_); + ; + std::vector yuyv_img(2 * width_ * height_); + if (!is_loaded) { + // Fill by dummy image + for (int y = 0; y < height_; ++y) { + for (int x = 0; x < 2 * width_; ++x) { + yuyv_img[2 * width_ * y + x] = (y * 2 * width_ + x) % 255; + } + } + } else { + + std::vector yuv(3 * width_ * width_); + rgb2YCrCb(yuv.data(), img, width_, height_); + + for (int y = 0; y < height_; ++y) { + for (int x = 0; x < width_; ++x) { + // Y + yuyv_img[2 * width_ * y + 2 * x + 0] = yuv[(x + y * width_) * 3]; + // Cb or Cr + yuyv_img[2 * width_ * y + 2 * x + 1] = ((x % 2) == 1) ? yuv[(y * width_ + x) * 3 + 1] : yuv[(y * width_ + x) * 3 + 2]; + } + } + } + memcpy(buf.data(), yuyv_img.data(), yuyv_img.size()); + ion::bb::image_io::image_cache[id_] = yuyv_img; + return; + } - template - void generate(Halide::Runtime::Buffer &buf) { + template + void generate(Halide::Runtime::Buffer& buf) + { - // Simulate frame interval - auto now = std::chrono::high_resolution_clock::now(); - auto actual_interval = std::chrono::duration_cast(now - checkpoint_).count(); - float expected_interval = 1e6f / static_cast(fps_); - if (actual_interval < expected_interval) { - usleep(expected_interval - actual_interval); - } + // Simulate frame interval + auto now = std::chrono::high_resolution_clock::now(); + auto actual_interval = std::chrono::duration_cast(now - checkpoint_).count(); + float expected_interval = 1e6f / static_cast(fps_); + if (actual_interval < expected_interval) { + usleep(expected_interval - actual_interval); + } - if (pixel_format_ == V4L2_PIX_FMT_YUYV) { - generate_yuyv(buf); - } else { - generate_bayer(buf); - } + if (pixel_format_ == V4L2_PIX_FMT_YUYV) { + generate_yuyv(buf); + } else { + generate_bayer(buf); + } - checkpoint_ = std::chrono::high_resolution_clock::now(); - } + checkpoint_ = std::chrono::high_resolution_clock::now(); + } - template - void get(Halide::Runtime::Buffer &buf) { - using namespace std; + template + void get(Halide::Runtime::Buffer& buf) + { + using namespace std; - if (sim_mode_) { - generate(buf); - return; - } + if (sim_mode_) { + generate(buf); + return; + } - if (buf.size_in_bytes() != buffer_size_) { - throw runtime_error("Bad buffer size"); - } + if (buf.size_in_bytes() != buffer_size_) { + throw runtime_error("Bad buffer size"); + } - /* queue-in buffer */ - if (-1 == xioctl(fd_, VIDIOC_QBUF, &next_buffer_)) { - log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_QBUF", errno, strerror(errno)); - sim_mode_ = true; - return; - } + /* queue-in buffer */ + if (-1 == xioctl(fd_, VIDIOC_QBUF, &next_buffer_)) { + log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_QBUF", errno, strerror(errno)); + sim_mode_ = true; + return; + } - epoll_event event; - if (-1 == epoll_wait(efd_, &event, 1, -1)) { - log::warn("Fallback to simulation mode: {} error {}, {}", "epoll_wait", errno, strerror(errno)); - sim_mode_ = true; - return; - } + epoll_event event; + if (-1 == epoll_wait(efd_, &event, 1, -1)) { + log::warn("Fallback to simulation mode: {} error {}, {}", "epoll_wait", errno, strerror(errno)); + sim_mode_ = true; + return; + } - if (event.data.fd != fd_) { - throw runtime_error("Unreachable"); - } + if (event.data.fd != fd_) { + throw runtime_error("Unreachable"); + } - struct v4l2_buffer v4l2_buf; - v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buf.memory = V4L2_MEMORY_USERPTR; + struct v4l2_buffer v4l2_buf; + v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_buf.memory = V4L2_MEMORY_USERPTR; + + if (-1 == xioctl(fd_, VIDIOC_DQBUF, &next_buffer_)) { + if (EAGAIN == errno) { + return; + } else { + log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_DQBUF", errno, strerror(errno)); + sim_mode_ = true; + return; + } + } - if (-1 == xioctl(fd_, VIDIOC_DQBUF, &next_buffer_)) { - if (EAGAIN == errno) { - return; - } else { - log::warn("Fallback to simulation mode: {} error {}, {}", "VIDIOC_DQBUF", errno, strerror(errno)); - sim_mode_ = true; - return; + memcpy(buf.data(), reinterpret_cast(next_buffer_.m.userptr), buf.size_in_bytes()); } - } - - memcpy(buf.data(), reinterpret_cast(next_buffer_.m.userptr), buf.size_in_bytes()); - } - -private: - int fd_; - std::vector buffers_; - v4l2_buffer next_buffer_; - - int32_t id_; - int32_t index_; - int32_t fps_; - int32_t width_; - int32_t height_; - uint32_t pixel_format_; - float gain_r_; - float gain_g_; - float gain_b_; - float offset_; - int32_t bit_width_; - int32_t bit_shift_; - bool sim_mode_; - std::string url_; - - int efd_; - - uint32_t buffer_size_; - - std::chrono::time_point checkpoint_; - - static std::unordered_map> instances_; -}; + private: + int fd_; + std::vector buffers_; + v4l2_buffer next_buffer_; + + int32_t id_; + int32_t index_; + int32_t fps_; + int32_t width_; + int32_t height_; + uint32_t pixel_format_; + float gain_r_; + float gain_g_; + float gain_b_; + float offset_; + int32_t bit_width_; + int32_t bit_shift_; + bool sim_mode_; + std::string url_; + + int efd_; + + uint32_t buffer_size_; + + std::chrono::time_point checkpoint_; + + static std::unordered_map> instances_; + }; -std::unordered_map> V4L2::instances_; + std::unordered_map> V4L2::instances_; -} // namespace image_io -} // namespace bb -} // namespace ion + } // namespace image_io +} // namespace bb +} // namespace ion extern "C" ION_EXPORT int ion_bb_image_io_v4l2( int32_t instance_id, @@ -677,11 +713,12 @@ extern "C" ION_EXPORT int ion_bb_image_io_v4l2( uint32_t pixel_format, uint32_t force_sim_mode, // Do not use bool to avoid LLVM codegen failure // Parameters for simulation - halide_buffer_t *url_buf, + halide_buffer_t* url_buf, float gain_r, float gain_g, float gain_b, float offset, int32_t bit_width, int32_t bit_shift, - halide_buffer_t *out) { + halide_buffer_t* out) +{ try { @@ -693,13 +730,13 @@ extern "C" ION_EXPORT int ion_bb_image_io_v4l2( return 0; } - auto &v4l2(ion::bb::image_io::V4L2::get_instance(instance_id, index, fps, width, height, pixel_format, gain_r, gain_g, gain_b, offset, bit_width, bit_shift, static_cast(force_sim_mode), reinterpret_cast(url_buf->host))); + auto& v4l2(ion::bb::image_io::V4L2::get_instance(instance_id, index, fps, width, height, pixel_format, gain_r, gain_g, gain_b, offset, bit_width, bit_shift, static_cast(force_sim_mode), reinterpret_cast(url_buf->host))); Halide::Runtime::Buffer obuf(*out); v4l2.get(obuf); return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -709,23 +746,24 @@ extern "C" ION_EXPORT int ion_bb_image_io_v4l2( } ION_REGISTER_EXTERN(ion_bb_image_io_v4l2) -extern "C" int ION_EXPORT ion_bb_image_io_camera(int32_t instance_id, int32_t index, int32_t fps, int32_t width, int32_t height, halide_buffer_t *url_buf, halide_buffer_t *out) { +extern "C" int ION_EXPORT ion_bb_image_io_camera(int32_t instance_id, int32_t index, int32_t fps, int32_t width, int32_t height, halide_buffer_t* url_buf, halide_buffer_t* out) +{ try { if (out->is_bounds_query()) { out->dim[0].min = 0; - out->dim[0].extent = 2 * width; // YUYV + out->dim[0].extent = 2 * width; // YUYV out->dim[1].min = 0; out->dim[1].extent = height; return 0; } - auto &v4l2(ion::bb::image_io::V4L2::get_instance(instance_id, index, fps, width, height, V4L2_PIX_FMT_YUYV, 1, 1, 1, 0, 8, 0, false, reinterpret_cast(url_buf->host))); + auto& v4l2(ion::bb::image_io::V4L2::get_instance(instance_id, index, fps, width, height, V4L2_PIX_FMT_YUYV, 1, 1, 1, 0, 8, 0, false, reinterpret_cast(url_buf->host))); Halide::Runtime::Buffer obuf(*out); v4l2.get(obuf); return 0; - } catch (const std::exception &e) { + } catch (const std::exception& e) { ion::log::error("Exception was thrown: {}", e.what()); return 1; } catch (...) { @@ -735,7 +773,4 @@ extern "C" int ION_EXPORT ion_bb_image_io_camera(int32_t instance_id, int32_t in } ION_REGISTER_EXTERN(ion_bb_image_io_camera) - - #endif // ION_BB_IMAGE_IO_RT_V4L2_H -