#include "track_sharing/kml_image_xml_stream.h" #include #include #include #include struct Image { std::string source; bool located = false; double lat = 0, lon = 0; }; struct Result { struct Coordinate { double lat, lon; bool gx, break_before; }; std::vector images; std::vector coordinates; size_t feature_start = 0; unsigned features = 0; track_sharing::KmlImageCallbacks callbacks() { return {this, [](void* p) { auto& r = *static_cast(p); r.feature_start = r.images.size(); ++r.features; }, [](void* p, std::string_view source) { auto& r = *static_cast(p); r.images.push_back({gps::kml::decodeImageSource(source)}); return true; }, [](void* p, bool located, double lat, double lon) { auto& r = *static_cast(p); for (size_t i = r.feature_start; i < r.images.size(); ++i) r.images[i].located = located, r.images[i].lat = lat, r.images[i].lon = lon; }, [](void* p, bool gx, double lat, double lon, double, bool, bool break_before) { static_cast(p)->coordinates.push_back({lat, lon, gx, break_before}); }}; } }; static bool parse(std::string_view xml, size_t chunk, Result& result) { char names[4096], source[4096]; track_sharing::KmlImageXmlStream parser(names, sizeof(names), source, sizeof(source), result.callbacks()); for (size_t i = 0; i < xml.size(); i += chunk) if (!parser.feed(reinterpret_cast(xml.data() + i), std::min(chunk, xml.size() - i))) return false; return parser.finish(); } static std::vector html(std::string_view text, size_t chunk, size_t capacity = 4096, bool stop = false) { char source[4096]; gps::kml::HtmlImageSourceStream parser(source, capacity); std::vector found; for (size_t i = 0; i < text.size(); i += chunk) parser.feed(text.substr(i, chunk), [&](std::string_view value) { found.push_back(gps::kml::decodeImageSource(value)); return !stop; }); return found; } int main() { const std::string input = "" "120.1234567,30.5,7 -->>]]>" "<img src="b&amp;c.jpg">121 31 12" "]]>" ""; for (size_t chunk = 1; chunk <= input.size(); ++chunk) { Result r; assert(parse(input, chunk, r)); assert(r.features == 3 && r.images.size() == 4); assert(r.images[0].source == "a"山.jpg" && r.images[1].source == "two.jpg"); assert(r.images[0].located && std::abs(r.images[0].lat - 30.5) < 1e-8 && std::abs(r.images[0].lon - 120.1234567) < 1e-8); assert(r.images[1].located && r.images[1].lat == r.images[0].lat); assert(r.images[2].source == "b&c.jpg" && r.images[2].located && std::abs(r.images[2].lon - 121) < 1e-8); assert(!r.images[3].located); } const std::vector>> cases = { {"", {}}, {"", {"a>b", "b.jpg/"}}, {"", {"good"}}, {"<img src=bad>", {"山😀"}}, {"", {"good"}}, {"" + std::string(100000, ", {"good"}}, {"", {"good"}}}; for (const auto& item : cases) for (size_t chunk : {size_t(1), size_t(2), size_t(17), size_t(256), item.first.size()}) assert(html(item.first, chunk) == item.second); assert(html("", 1, 4) == std::vector({"1234", "x"})); assert(html("", 1, 4096, true) == std::vector({"a"})); char source[16]; gps::kml::HtmlImageSourceStream reset(source, sizeof(source)); std::vector found; auto collect = [&](std::string_view v) { found.emplace_back(v); return true; }; reset.feed("", collect); assert(found == std::vector({"good"})); const auto check = [](const std::string& doc, bool expected, size_t images) { for (size_t chunk : {size_t(1), size_t(7), doc.size()}) { Result r; assert(parse(doc, chunk, r) == expected); if (expected) assert(r.images.size() == images); } }; check("]]>1,2", true, 1); check("]]>]]>]]>", true, 1); check("]]>]]>", true, 1); check("", false, 0); check("", false, 0); check("]]>", false, 0); check("", false, 0); check("", false, 0); check("", false, 0); check("&bad;", false, 0); check("", false, 0); check("1,2,", false, 0); check("1 2", false, 0); check("NaN,2", false, 0); check("1e999,2", false, 0); std::string deep = ""; for (unsigned i = 0; i < 64; ++i) deep += ""; for (unsigned i = 0; i < 64; ++i) deep += ""; check(deep + "", false, 0); std::string over_points = ""; for (unsigned i = 0; i < 100001; ++i) over_points += "1,2 "; check(over_points + "", false, 0); check("]]>", true, 1); Result invalid; assert(!parse("181,2]]>", 1, invalid)); Result shadow; assert(parse("1,2]]>", 1, shadow)); assert(!shadow.images[0].located); Result polygon; assert(parse("1,2 2,3 3,4 1,2]]>", 1, polygon)); assert(polygon.images[0].located && std::abs(polygon.images[0].lon - 1) < 1e-8); Result segmented; assert(parse("1,2 2,33,4 4,55 6 06 7 07 8 08 9 0", 1, segmented)); assert(segmented.coordinates.size() == 8); const bool breaks[] = {true, false, true, false, true, true, false, true}; for (size_t i = 0; i < 8; ++i) assert(segmented.coordinates[i].break_before == breaks[i]); }