1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #include "include/rados/librados.h"
4 #include "include/rados/librados.hpp"
5 #include "include/stringify.h"
6 #include "test/librados/test.h"
7 #include "test/librados/test_common.h"
8 #include "test/librados/TestCase.h"
9 #include "global/global_context.h"
11 #include "include/types.h"
12 #include "common/hobject.h"
13 #include "gtest/gtest.h"
18 using namespace librados
;
20 typedef RadosTestNSCleanup LibRadosList
;
21 typedef RadosTestECNSCleanup LibRadosListEC
;
22 typedef RadosTestNP LibRadosListNP
;
25 TEST_F(LibRadosList
, ListObjects
) {
27 memset(buf
, 0xcc, sizeof(buf
));
28 ASSERT_EQ(0, rados_write(ioctx
, "foo", buf
, sizeof(buf
), 0));
30 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
33 while (rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
) != -ENOENT
) {
35 ASSERT_EQ(std::string(entry
), "foo");
38 rados_nobjects_list_close(ctx
);
41 TEST_F(LibRadosList
, ListObjectsZeroInName
) {
43 memset(buf
, 0xcc, sizeof(buf
));
44 ASSERT_EQ(0, rados_write(ioctx
, "foo\0bar", buf
, sizeof(buf
), 0));
46 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
50 while (rados_nobjects_list_next2(ctx
, &entry
, NULL
, NULL
,
51 &entry_size
, NULL
, NULL
) != -ENOENT
) {
53 ASSERT_EQ(std::string(entry
, entry_size
), "foo\0bar");
56 rados_nobjects_list_close(ctx
);
59 static void check_list(
60 std::set
<std::string
>& myset
,
61 rados_list_ctx_t
& ctx
,
62 const std::string
&check_nspace
)
64 const char *entry
, *nspace
;
65 cout
<< "myset " << myset
<< std::endl
;
66 // we should see every item exactly once.
68 while ((ret
= rados_nobjects_list_next(ctx
, &entry
, NULL
, &nspace
)) == 0) {
69 std::string test_name
;
70 if (check_nspace
== all_nspaces
) {
71 test_name
= std::string(nspace
) + ":" + std::string(entry
);
73 ASSERT_TRUE(std::string(nspace
) == check_nspace
);
74 test_name
= std::string(entry
);
76 cout
<< test_name
<< std::endl
;
78 ASSERT_TRUE(myset
.end() != myset
.find(test_name
));
79 myset
.erase(test_name
);
81 ASSERT_EQ(-ENOENT
, ret
);
82 ASSERT_TRUE(myset
.empty());
85 TEST_F(LibRadosList
, ListObjectsNS
) {
87 memset(buf
, 0xcc, sizeof(buf
));
88 // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
89 rados_ioctx_set_namespace(ioctx
, "");
90 ASSERT_EQ(0, rados_write(ioctx
, "foo1", buf
, sizeof(buf
), 0));
91 rados_ioctx_set_namespace(ioctx
, "ns1");
92 ASSERT_EQ(0, rados_write(ioctx
, "foo1", buf
, sizeof(buf
), 0));
93 rados_ioctx_set_namespace(ioctx
, "");
94 ASSERT_EQ(0, rados_write(ioctx
, "foo2", buf
, sizeof(buf
), 0));
95 ASSERT_EQ(0, rados_write(ioctx
, "foo3", buf
, sizeof(buf
), 0));
96 rados_ioctx_set_namespace(ioctx
, "ns1");
97 ASSERT_EQ(0, rados_write(ioctx
, "foo4", buf
, sizeof(buf
), 0));
98 ASSERT_EQ(0, rados_write(ioctx
, "foo5", buf
, sizeof(buf
), 0));
99 rados_ioctx_set_namespace(ioctx
, "ns2");
100 ASSERT_EQ(0, rados_write(ioctx
, "foo6", buf
, sizeof(buf
), 0));
101 ASSERT_EQ(0, rados_write(ioctx
, "foo7", buf
, sizeof(buf
), 0));
104 ASSERT_EQ(-ERANGE
, rados_ioctx_get_namespace(ioctx
, nspace
, 3));
105 ASSERT_EQ(static_cast<int>(strlen("ns2")),
106 rados_ioctx_get_namespace(ioctx
, nspace
, sizeof(nspace
)));
107 ASSERT_EQ(0, strcmp("ns2", nspace
));
109 std::set
<std::string
> def
, ns1
, ns2
, all
;
110 def
.insert(std::string("foo1"));
111 def
.insert(std::string("foo2"));
112 def
.insert(std::string("foo3"));
113 ns1
.insert(std::string("foo1"));
114 ns1
.insert(std::string("foo4"));
115 ns1
.insert(std::string("foo5"));
116 ns2
.insert(std::string("foo6"));
117 ns2
.insert(std::string("foo7"));
118 all
.insert(std::string(":foo1"));
119 all
.insert(std::string(":foo2"));
120 all
.insert(std::string(":foo3"));
121 all
.insert(std::string("ns1:foo1"));
122 all
.insert(std::string("ns1:foo4"));
123 all
.insert(std::string("ns1:foo5"));
124 all
.insert(std::string("ns2:foo6"));
125 all
.insert(std::string("ns2:foo7"));
127 rados_list_ctx_t ctx
;
128 // Check default namespace ""
129 rados_ioctx_set_namespace(ioctx
, "");
130 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
131 check_list(def
, ctx
, "");
132 rados_nobjects_list_close(ctx
);
134 // Check namespace "ns1"
135 rados_ioctx_set_namespace(ioctx
, "ns1");
136 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
137 check_list(ns1
, ctx
, "ns1");
138 rados_nobjects_list_close(ctx
);
140 // Check namespace "ns2"
141 rados_ioctx_set_namespace(ioctx
, "ns2");
142 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
143 check_list(ns2
, ctx
, "ns2");
144 rados_nobjects_list_close(ctx
);
146 // Check ALL namespaces
147 rados_ioctx_set_namespace(ioctx
, LIBRADOS_ALL_NSPACES
);
148 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
149 check_list(all
, ctx
, all_nspaces
);
150 rados_nobjects_list_close(ctx
);
154 TEST_F(LibRadosList
, ListObjectsStart
) {
156 memset(buf
, 0xcc, sizeof(buf
));
158 for (int i
=0; i
<16; ++i
) {
159 string n
= stringify(i
);
160 ASSERT_EQ(0, rados_write(ioctx
, n
.c_str(), buf
, sizeof(buf
), 0));
163 rados_list_ctx_t ctx
;
164 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
165 std::map
<int, std::set
<std::string
> > pg_to_obj
;
167 while (rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
) == 0) {
168 uint32_t pos
= rados_nobjects_list_get_pg_hash_position(ctx
);
169 std::cout
<< entry
<< " " << pos
<< std::endl
;
170 pg_to_obj
[pos
].insert(entry
);
172 rados_nobjects_list_close(ctx
);
174 std::map
<int, std::set
<std::string
> >::reverse_iterator p
=
176 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
177 while (p
!= pg_to_obj
.rend()) {
178 ASSERT_EQ((uint32_t)p
->first
, rados_nobjects_list_seek(ctx
, p
->first
));
179 ASSERT_EQ(0, rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
));
180 std::cout
<< "have " << entry
<< " expect one of " << p
->second
<< std::endl
;
181 ASSERT_TRUE(p
->second
.count(entry
));
184 rados_nobjects_list_close(ctx
);
187 // this function replicates
188 // librados::operator<<(std::ostream& os, const librados::ObjectCursor& oc)
189 // because we don't want to use librados in librados client.
190 std::ostream
& operator<<(std::ostream
&os
, const rados_object_list_cursor
& oc
)
193 os
<< *(hobject_t
*)oc
;
200 TEST_F(LibRadosList
, ListObjectsCursor
) {
202 memset(buf
, 0xcc, sizeof(buf
));
204 const int max_objs
= 16;
206 for (int i
=0; i
<max_objs
; ++i
) {
207 string n
= stringify(i
);
208 ASSERT_EQ(0, rados_write(ioctx
, n
.c_str(), buf
, sizeof(buf
), 0));
212 rados_list_ctx_t ctx
;
214 rados_object_list_cursor cursor
;
215 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
216 ASSERT_EQ(rados_nobjects_list_get_cursor(ctx
, &cursor
), 0);
217 rados_object_list_cursor first_cursor
= cursor
;
218 cout
<< "x cursor=" << cursor
<< std::endl
;
219 while (rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
) == 0) {
221 ASSERT_EQ(rados_nobjects_list_get_cursor(ctx
, &cursor
), 0);
222 cout
<< "> oid=" << oid
<< " cursor=" << cursor
<< std::endl
;
224 rados_nobjects_list_seek_cursor(ctx
, first_cursor
);
225 ASSERT_EQ(rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
), 0);
226 cout
<< "FIRST> seek to " << first_cursor
<< " oid=" << string(entry
) << std::endl
;
228 rados_list_ctx_t ctx
;
229 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
231 std::map
<rados_object_list_cursor
, string
> cursor_to_obj
;
235 while (rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
) == 0) {
236 rados_object_list_cursor cursor
;
237 ASSERT_EQ(rados_nobjects_list_get_cursor(ctx
, &cursor
), 0);
239 cout
<< ": oid=" << oid
<< " cursor=" << cursor
<< std::endl
;
240 cursor_to_obj
[cursor
] = oid
;
242 rados_nobjects_list_seek_cursor(ctx
, cursor
);
243 cout
<< ": seek to " << cursor
<< std::endl
;
244 ASSERT_EQ(rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
), 0);
245 cout
<< "> " << cursor
<< " -> " << entry
<< std::endl
;
246 ASSERT_EQ(string(entry
), oid
);
247 ASSERT_LT(count
, max_objs
); /* avoid infinite loops due to bad seek */
252 ASSERT_EQ(count
, max_objs
);
254 auto p
= cursor_to_obj
.rbegin();
255 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
256 while (p
!= cursor_to_obj
.rend()) {
257 cout
<< ": seek to " << p
->first
<< std::endl
;
258 rados_object_list_cursor cursor
;
259 rados_object_list_cursor
oid(p
->first
);
260 rados_nobjects_list_seek_cursor(ctx
, oid
);
261 ASSERT_EQ(rados_nobjects_list_get_cursor(ctx
, &cursor
), 0);
262 cout
<< ": cursor()=" << cursor
<< " expected=" << oid
<< std::endl
;
263 // ASSERT_EQ(ObjectCursor(oid), ObjectCursor(cursor));
264 ASSERT_EQ(rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
), 0);
265 cout
<< "> " << cursor
<< " -> " << entry
<< std::endl
;
266 cout
<< ": entry=" << entry
<< " expected=" << p
->second
<< std::endl
;
267 ASSERT_EQ(p
->second
, string(entry
));
271 rados_object_list_cursor_free(ctx
, cursor
);
275 TEST_F(LibRadosListEC
, ListObjects
) {
277 memset(buf
, 0xcc, sizeof(buf
));
278 ASSERT_EQ(0, rados_write(ioctx
, "foo", buf
, sizeof(buf
), 0));
279 rados_list_ctx_t ctx
;
280 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
282 bool foundit
= false;
283 while (rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
) != -ENOENT
) {
285 ASSERT_EQ(std::string(entry
), "foo");
287 ASSERT_TRUE(foundit
);
288 rados_nobjects_list_close(ctx
);
291 TEST_F(LibRadosListEC
, ListObjectsNS
) {
293 memset(buf
, 0xcc, sizeof(buf
));
294 // Create :foo1, :foo2, :foo3, n1:foo1, ns1:foo4, ns1:foo5, ns2:foo6, n2:foo7
295 rados_ioctx_set_namespace(ioctx
, "");
296 ASSERT_EQ(0, rados_write(ioctx
, "foo1", buf
, sizeof(buf
), 0));
297 rados_ioctx_set_namespace(ioctx
, "ns1");
298 ASSERT_EQ(0, rados_write(ioctx
, "foo1", buf
, sizeof(buf
), 0));
299 rados_ioctx_set_namespace(ioctx
, "");
300 ASSERT_EQ(0, rados_write(ioctx
, "foo2", buf
, sizeof(buf
), 0));
301 ASSERT_EQ(0, rados_write(ioctx
, "foo3", buf
, sizeof(buf
), 0));
302 rados_ioctx_set_namespace(ioctx
, "ns1");
303 ASSERT_EQ(0, rados_write(ioctx
, "foo4", buf
, sizeof(buf
), 0));
304 ASSERT_EQ(0, rados_write(ioctx
, "foo5", buf
, sizeof(buf
), 0));
305 rados_ioctx_set_namespace(ioctx
, "ns2");
306 ASSERT_EQ(0, rados_write(ioctx
, "foo6", buf
, sizeof(buf
), 0));
307 ASSERT_EQ(0, rados_write(ioctx
, "foo7", buf
, sizeof(buf
), 0));
309 std::set
<std::string
> def
, ns1
, ns2
, all
;
310 def
.insert(std::string("foo1"));
311 def
.insert(std::string("foo2"));
312 def
.insert(std::string("foo3"));
313 ns1
.insert(std::string("foo1"));
314 ns1
.insert(std::string("foo4"));
315 ns1
.insert(std::string("foo5"));
316 ns2
.insert(std::string("foo6"));
317 ns2
.insert(std::string("foo7"));
318 all
.insert(std::string(":foo1"));
319 all
.insert(std::string(":foo2"));
320 all
.insert(std::string(":foo3"));
321 all
.insert(std::string("ns1:foo1"));
322 all
.insert(std::string("ns1:foo4"));
323 all
.insert(std::string("ns1:foo5"));
324 all
.insert(std::string("ns2:foo6"));
325 all
.insert(std::string("ns2:foo7"));
327 rados_list_ctx_t ctx
;
328 // Check default namespace ""
329 rados_ioctx_set_namespace(ioctx
, "");
330 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
331 check_list(def
, ctx
, "");
332 rados_nobjects_list_close(ctx
);
334 // Check default namespace "ns1"
335 rados_ioctx_set_namespace(ioctx
, "ns1");
336 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
337 check_list(ns1
, ctx
, "ns1");
338 rados_nobjects_list_close(ctx
);
340 // Check default namespace "ns2"
341 rados_ioctx_set_namespace(ioctx
, "ns2");
342 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
343 check_list(ns2
, ctx
, "ns2");
344 rados_nobjects_list_close(ctx
);
346 // Check all namespaces
347 rados_ioctx_set_namespace(ioctx
, LIBRADOS_ALL_NSPACES
);
348 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
349 check_list(all
, ctx
, all_nspaces
);
350 rados_nobjects_list_close(ctx
);
354 TEST_F(LibRadosListEC
, ListObjectsStart
) {
356 memset(buf
, 0xcc, sizeof(buf
));
358 for (int i
=0; i
<16; ++i
) {
359 string n
= stringify(i
);
360 ASSERT_EQ(0, rados_write(ioctx
, n
.c_str(), buf
, sizeof(buf
), 0));
363 rados_list_ctx_t ctx
;
364 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
365 std::map
<int, std::set
<std::string
> > pg_to_obj
;
367 while (rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
) == 0) {
368 uint32_t pos
= rados_nobjects_list_get_pg_hash_position(ctx
);
369 std::cout
<< entry
<< " " << pos
<< std::endl
;
370 pg_to_obj
[pos
].insert(entry
);
372 rados_nobjects_list_close(ctx
);
374 std::map
<int, std::set
<std::string
> >::reverse_iterator p
=
376 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
377 while (p
!= pg_to_obj
.rend()) {
378 ASSERT_EQ((uint32_t)p
->first
, rados_nobjects_list_seek(ctx
, p
->first
));
379 ASSERT_EQ(0, rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
));
380 std::cout
<< "have " << entry
<< " expect one of " << p
->second
<< std::endl
;
381 ASSERT_TRUE(p
->second
.count(entry
));
384 rados_nobjects_list_close(ctx
);
387 TEST_F(LibRadosListNP
, ListObjectsError
) {
388 std::string pool_name
;
391 pool_name
= get_temp_pool_name();
392 ASSERT_EQ("", create_one_pool(pool_name
, &cluster
));
393 ASSERT_EQ(0, rados_ioctx_create(cluster
, pool_name
.c_str(), &ioctx
));
395 memset(buf
, 0xcc, sizeof(buf
));
396 rados_ioctx_set_namespace(ioctx
, "");
397 ASSERT_EQ(0, rados_write(ioctx
, "foo", buf
, sizeof(buf
), 0));
399 //ASSERT_EQ(0, rados_pool_delete(cluster, pool_name.c_str()));
402 size_t buflen
, stlen
;
403 string c
= "{\"prefix\":\"osd pool rm\",\"pool\": \"" + pool_name
+
404 "\",\"pool2\":\"" + pool_name
+
405 "\",\"yes_i_really_really_mean_it_not_faking\": true}";
406 const char *cmd
[2] = { c
.c_str(), 0 };
407 ASSERT_EQ(0, rados_mon_command(cluster
, (const char **)cmd
, 1, "", 0, &buf
, &buflen
, &st
, &stlen
));
408 ASSERT_EQ(0, rados_wait_for_latest_osdmap(cluster
));
411 rados_list_ctx_t ctx
;
412 ASSERT_EQ(0, rados_nobjects_list_open(ioctx
, &ctx
));
414 ASSERT_EQ(-ENOENT
, rados_nobjects_list_next(ctx
, &entry
, NULL
, NULL
));
415 rados_nobjects_list_close(ctx
);
416 rados_ioctx_destroy(ioctx
);
417 rados_shutdown(cluster
);
422 // ---------------------------------------------
424 TEST_F(LibRadosList
, EnumerateObjects
) {
426 memset(buf
, 0xcc, sizeof(buf
));
428 const uint32_t n_objects
= 16;
429 for (unsigned i
=0; i
<n_objects
; ++i
) {
430 ASSERT_EQ(0, rados_write(ioctx
, stringify(i
).c_str(), buf
, sizeof(buf
), 0));
433 // Ensure a non-power-of-two PG count to avoid only
434 // touching the easy path.
435 ASSERT_TRUE(set_pg_num(&s_cluster
, pool_name
, 11).empty());
436 ASSERT_TRUE(set_pgp_num(&s_cluster
, pool_name
, 11).empty());
438 std::set
<std::string
> saw_obj
;
439 rados_object_list_cursor c
= rados_object_list_begin(ioctx
);
440 rados_object_list_cursor end
= rados_object_list_end(ioctx
);
441 while(!rados_object_list_is_end(ioctx
, c
))
443 rados_object_list_item results
[12];
444 memset(results
, 0, sizeof(rados_object_list_item
) * 12);
445 rados_object_list_cursor temp_end
= rados_object_list_end(ioctx
);
446 int r
= rados_object_list(ioctx
, c
, temp_end
,
447 12, NULL
, 0, results
, &c
);
448 rados_object_list_cursor_free(ioctx
, temp_end
);
450 for (int i
= 0; i
< r
; ++i
) {
451 std::string
oid(results
[i
].oid
, results
[i
].oid_length
);
452 if (saw_obj
.count(oid
)) {
453 std::cerr
<< "duplicate obj " << oid
<< std::endl
;
455 ASSERT_FALSE(saw_obj
.count(oid
));
458 rados_object_list_free(12, results
);
460 rados_object_list_cursor_free(ioctx
, c
);
461 rados_object_list_cursor_free(ioctx
, end
);
463 for (unsigned i
=0; i
<n_objects
; ++i
) {
464 if (!saw_obj
.count(stringify(i
))) {
465 std::cerr
<< "missing object " << i
<< std::endl
;
467 ASSERT_TRUE(saw_obj
.count(stringify(i
)));
469 ASSERT_EQ(n_objects
, saw_obj
.size());
472 TEST_F(LibRadosList
, EnumerateObjectsSplit
) {
474 memset(buf
, 0xcc, sizeof(buf
));
476 const uint32_t n_objects
= 16;
477 for (unsigned i
=0; i
<n_objects
; ++i
) {
478 ASSERT_EQ(0, rados_write(ioctx
, stringify(i
).c_str(), buf
, sizeof(buf
), 0));
481 // Ensure a non-power-of-two PG count to avoid only
482 // touching the easy path.
483 ASSERT_TRUE(set_pg_num(&s_cluster
, pool_name
, 11).empty());
484 ASSERT_TRUE(set_pgp_num(&s_cluster
, pool_name
, 11).empty());
486 rados_object_list_cursor begin
= rados_object_list_begin(ioctx
);
487 rados_object_list_cursor end
= rados_object_list_end(ioctx
);
489 // Step through an odd number of shards
491 std::set
<std::string
> saw_obj
;
492 for (unsigned n
= 0; n
< m
; ++n
) {
493 rados_object_list_cursor shard_start
= rados_object_list_begin(ioctx
);;
494 rados_object_list_cursor shard_end
= rados_object_list_end(ioctx
);;
496 rados_object_list_slice(
504 std::cout
<< "split " << n
<< "/" << m
<< " -> "
505 << *(hobject_t
*)shard_start
<< " "
506 << *(hobject_t
*)shard_end
<< std::endl
;
508 rados_object_list_cursor c
= shard_start
;
509 //while(c < shard_end)
510 while(rados_object_list_cursor_cmp(ioctx
, c
, shard_end
) == -1)
512 rados_object_list_item results
[12];
513 memset(results
, 0, sizeof(rados_object_list_item
) * 12);
514 int r
= rados_object_list(ioctx
,
516 12, NULL
, 0, results
, &c
);
518 for (int i
= 0; i
< r
; ++i
) {
519 std::string
oid(results
[i
].oid
, results
[i
].oid_length
);
520 if (saw_obj
.count(oid
)) {
521 std::cerr
<< "duplicate obj " << oid
<< std::endl
;
523 ASSERT_FALSE(saw_obj
.count(oid
));
526 rados_object_list_free(12, results
);
528 rados_object_list_cursor_free(ioctx
, shard_start
);
529 rados_object_list_cursor_free(ioctx
, shard_end
);
532 rados_object_list_cursor_free(ioctx
, begin
);
533 rados_object_list_cursor_free(ioctx
, end
);
535 for (unsigned i
=0; i
<n_objects
; ++i
) {
536 if (!saw_obj
.count(stringify(i
))) {
537 std::cerr
<< "missing object " << i
<< std::endl
;
539 ASSERT_TRUE(saw_obj
.count(stringify(i
)));
541 ASSERT_EQ(n_objects
, saw_obj
.size());