1 #include "catch.hpp"
2 
3 #include <osmium/builder/attr.hpp>
4 #include <osmium/storage/item_stash.hpp>
5 
6 #include <sstream>
7 #include <string>
8 #include <vector>
9 
generate_test_data()10 osmium::memory::Buffer generate_test_data() {
11     using namespace osmium::builder::attr; // NOLINT(google-build-using-namespace)
12 
13     osmium::memory::Buffer buffer{1024UL * 1024UL, osmium::memory::Buffer::auto_grow::yes};
14 
15     const osmium::object_id_type num_nodes     = 100;
16     const osmium::object_id_type num_ways      =  50;
17     const osmium::object_id_type num_relations =  30;
18 
19     osmium::object_id_type id = 1;
20     for (; id <= num_nodes; ++id) {
21         osmium::builder::add_node(buffer, _id(id));
22     }
23 
24     for (; id <= num_nodes + num_ways; ++id) {
25         osmium::builder::add_way(buffer, _id(id));
26     }
27 
28     for (; id <= num_nodes + num_ways + num_relations; ++id) {
29         osmium::builder::add_relation(buffer, _id(id));
30     }
31 
32     return buffer;
33 }
34 
35 
36 TEST_CASE("Item stash handle") {
37     const auto handle = osmium::ItemStash::handle_type{};
38     REQUIRE_FALSE(handle.valid());
39 
40     std::stringstream ss;
41     ss << handle;
42     REQUIRE(ss.str() == "-");
43 }
44 
45 TEST_CASE("Item stash") {
46     const auto buffer = generate_test_data();
47 
48     osmium::ItemStash stash;
49     REQUIRE(stash.size() == 0);
50     REQUIRE(stash.count_removed() == 0);
51 
52     std::vector<osmium::ItemStash::handle_type> handles;
53     for (const auto& item : buffer) {
54         auto handle = stash.add_item(item);
55         handles.push_back(handle);
56     }
57 
58     REQUIRE(stash.size() == 180);
59     REQUIRE(stash.count_removed() == 0);
60 
61     REQUIRE(stash.used_memory() > 1024UL * 1024UL);
62 
63     osmium::object_id_type id = 1;
64     for (auto& handle : handles) { // must be reference because we will change it!
65         REQUIRE(handle.valid());
66         const auto& item = stash.get_item(handle);
67         bool correct_type = item.type() == osmium::item_type::node ||
68                             item.type() == osmium::item_type::way ||
69                             item.type() == osmium::item_type::relation;
70         REQUIRE(correct_type);
71         const auto& obj = static_cast<const osmium::OSMObject&>(item);
72         REQUIRE(obj.id() == id);
73 
74         std::stringstream ss;
75         ss << handle;
76         REQUIRE(ss.str() == std::to_string(id));
77 
78         if (obj.id() % 3 == 0) {
79             stash.remove_item(handle);
80             handle = osmium::ItemStash::handle_type{};
81         }
82 
83         ++id;
84     }
85 
86     REQUIRE(stash.size() == 120);
87     REQUIRE(stash.count_removed() == 60);
88 
89     id = 1;
90     int count_valid   = 0;
91     int count_invalid = 0;
92     for (auto handle : handles) {
93         if (handle.valid()) {
94             ++count_valid;
95             const auto& item = stash.get_item(handle);
96             const bool correct_type = item.type() == osmium::item_type::node ||
97                                       item.type() == osmium::item_type::way ||
98                                       item.type() == osmium::item_type::relation;
99             REQUIRE(correct_type);
100             const auto& obj = static_cast<const osmium::OSMObject&>(item);
101             REQUIRE(obj.id() == id);
102         } else {
103             ++count_invalid;
104         }
105         ++id;
106     }
107 
108     REQUIRE(count_valid   == 120);
109     REQUIRE(count_invalid ==  60);
110 
111     stash.garbage_collect();
112     REQUIRE(stash.size() == 120);
113     REQUIRE(stash.count_removed() == 0);
114 
115     id = 1;
116     for (auto handle : handles) {
117         if (handle.valid()) {
118             const auto& item = stash.get_item(handle);
119             const bool correct_type = item.type() == osmium::item_type::node ||
120                                       item.type() == osmium::item_type::way ||
121                                       item.type() == osmium::item_type::relation;
122             REQUIRE(correct_type);
123             const auto& obj = static_cast<const osmium::OSMObject&>(item);
124             REQUIRE(obj.id() == id);
125         }
126         ++id;
127     }
128 
129     stash.clear();
130     REQUIRE(stash.size() == 0);
131     REQUIRE(stash.count_removed() == 0);
132 }
133 
134 TEST_CASE("Fill item stash until it garbage collects") {
135     const auto buffer = generate_test_data();
136 
137     osmium::ItemStash stash;
138     REQUIRE(stash.size() == 0);
139     REQUIRE(stash.count_removed() == 0);
140 
141     const auto& node = buffer.get<osmium::Node>(0);
142 
143     std::vector<osmium::ItemStash::handle_type> handles;
144     std::size_t num_items = 6UL * 1000UL * 1000UL;
145     for (std::size_t i = 0; i < num_items; ++i) {
146         auto handle = stash.add_item(node);
147         handles.push_back(handle);
148     }
149 
150     REQUIRE(stash.size() == num_items);
151     REQUIRE(stash.count_removed() == 0);
152 
153     for (std::size_t i = 0; i < num_items; ++i) {
154         if (i % 10 != 0) {
155             stash.remove_item(handles[i]);
156         }
157     }
158 
159     REQUIRE(stash.size() == num_items / 10);
160     REQUIRE(stash.count_removed() == num_items / 10 * 9);
161 
162     // trigger compaction
163     stash.add_item(node);
164 
165     REQUIRE(stash.size() == num_items / 10 + 1);
166     REQUIRE(stash.count_removed() == 0);
167 }
168 
169