2 * Copyright (C) 2007-2009 Andre Noll <maan@tuebingen.mpg.de>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file osl.c Object storage layer functions. */
8 #include <dirent.h> /* readdir() */
17 * Taken from Drepper: How to write shared libraries, Appendix B.
19 * The main reason for this rather fancy implementation of strerror() is to
20 * avoid having an array of pointers. This is desirable because initialized
21 * pointer variables increase the startup time of the library due to the
22 * processing of relocations.
25 #define MSGSTRFIELD(line) MSGSTRFIELD1(line)
26 #define MSGSTRFIELD1(line) str##line
27 static const union msgstr_t
{
29 #define OSL_ERROR(n, s) char MSGSTRFIELD(__LINE__)[sizeof(s)];
35 #define OSL_ERROR(n, s) s,
39 static const unsigned int errmsgidx
[] = {
40 #define OSL_ERROR(n, s) [n] = offsetof(union msgstr_t, MSGSTRFIELD(__LINE__)),
45 __export
const char *osl_strerror(int num
)
47 return msgstr
.str
+ errmsgidx
[num
];
52 static void __attribute ((constructor
)) init_loglevel(void)
54 char *p
= getenv("OSL_LOGLEVEL");
56 /* don't log anything if unset */
57 loglevel
= p
? atoi(p
) : EMERG
+ 1;
64 * \param fmt Usual format string.
66 * All XXX_LOG() macros use this function.
68 __printf_2_3
void __log(int ll
, const char* fmt
,...)
79 strftime(str
, sizeof(str
), "%b %d %H:%M:%S", tm
);
80 fprintf(stderr
, "%s ", str
);
82 vfprintf(stderr
, fmt
, argp
);
87 * A wrapper for lseek(2).
89 * \param fd The file descriptor whose offset is to be to repositioned.
90 * \param offset A value-result parameter.
91 * \param whence Usual repositioning directive.
93 * Reposition the offset of the file descriptor \a fd to the argument \a offset
94 * according to the directive \a whence. Upon successful return, \a offset
95 * contains the resulting offset location as measured in bytes from the
96 * beginning of the file.
98 * \return Positive on success. Otherwise, the function returns \p -E_OSL_LSEEK.
102 static int __lseek(int fd
, off_t
*offset
, int whence
)
104 *offset
= lseek(fd
, *offset
, whence
);
105 int ret
= -E_OSL_LSEEK
;
111 static int append_file(const char *filename
, char *data
, size_t data_size
,
116 // DEBUG_LOG("appending %zu + %zu bytes\n", header_size, data_size);
117 ret
= osl_open(filename
, O_WRONLY
| O_CREAT
| O_APPEND
, 0644);
121 ret
= write_all(fd
, data
, &data_size
);
126 ret
= __lseek(fd
, &offset
, SEEK_END
);
129 // DEBUG_LOG("new file size: " FMT_OFF_T "\n", offset);
138 static int verify_name(const char *name
)
141 return -E_OSL_BAD_NAME
;
143 return -E_OSL_BAD_NAME
;
144 if (strchr(name
, '/'))
145 return -E_OSL_BAD_NAME
;
146 if (!strcmp(name
, ".."))
147 return -E_OSL_BAD_NAME
;
148 if (!strcmp(name
, "."))
149 return -E_OSL_BAD_NAME
;
153 static char *disk_storage_dirname(const struct osl_table
*t
, unsigned col_num
,
156 char *dirname
, *column_name
= column_filename(t
, col_num
);
160 if (!(t
->desc
->flags
& OSL_LARGE_TABLE
))
162 dirname
= make_message("%s/%.2s", column_name
, ds_name
);
167 static char *disk_storage_name_of_object(const struct osl_table
*t
,
168 const struct osl_object
*obj
)
170 HASH_TYPE hash
[HASH_SIZE
];
171 hash_object(t
, obj
, hash
);
172 return disk_storage_name_of_hash(t
, hash
);
175 static int disk_storage_name_of_row(const struct osl_table
*t
,
176 const struct osl_row
*row
, char **name
)
178 struct osl_object obj
;
179 int ret
= osl_get_object(t
, row
, t
->disk_storage_name_column
, &obj
);
183 *name
= disk_storage_name_of_object(t
, &obj
);
189 static void column_name_hash(const struct osl_table
*t
, const char *col_name
,
192 hash_function(t
->version
, col_name
, strlen(col_name
), hash
);
195 static int init_column_descriptions(struct osl_table
*t
)
198 const struct osl_column_description
*cd
;
200 ret
= verify_name(t
->desc
->name
);
203 ret
= -E_OSL_BAD_DB_DIR
;
204 if (!t
->desc
->dir
&& (t
->num_disk_storage_columns
|| t
->num_mapped_columns
))
206 /* the size of the index header without column descriptions */
207 t
->index_header_size
= IDX_COLUMN_DESCRIPTIONS
;
208 FOR_EACH_COLUMN(i
, t
->desc
, cd
) {
209 if (cd
->storage_flags
& OSL_RBTREE
) {
210 if (!cd
->compare_function
)
211 return -E_OSL_NO_COMPARE_FUNC
;
213 if (cd
->storage_type
== OSL_NO_STORAGE
)
215 ret
= -E_OSL_NO_COLUMN_NAME
;
216 if (!cd
->name
|| !cd
->name
[0])
218 ret
= verify_name(cd
->name
);
221 t
->index_header_size
+= index_column_description_size(cd
->name
);
222 ret
= -E_OSL_DUPLICATE_COL_NAME
;
223 for (j
= i
+ 1; j
< t
->desc
->num_columns
; j
++) {
224 const char *name2
= get_column_description(t
->desc
,
226 if (cd
->name
&& name2
&& !strcmp(cd
->name
, name2
))
236 * Initialize a struct table from given table description.
238 * \param desc The description of the osl table.
239 * \param table_ptr Result is returned here.
241 * This function performs several sanity checks on \p desc and returns if any
242 * of these tests fail. On success, a struct \p osl_table is allocated and
243 * initialized with data derived from \p desc.
247 * \sa struct osl_table.
249 int init_table_structure(const struct osl_table_description
*desc
,
250 struct osl_table
**table_ptr
)
252 const struct osl_column_description
*cd
;
253 struct osl_table
*t
= calloc(1, sizeof(*t
));
254 int i
, ret
= -E_OSL_NOMEM
, have_disk_storage_name_column
= 0;
258 ret
= -E_OSL_BAD_TABLE_DESC
;
261 DEBUG_LOG("creating table structure for '%s' from table "
262 "description\n", desc
->name
);
263 ret
= -E_OSL_NO_COLUMN_DESC
;
264 if (!desc
->column_descriptions
)
266 ret
= -E_OSL_NO_COLUMNS
;
267 if (!desc
->num_columns
)
270 t
->columns
= calloc(desc
->num_columns
, sizeof(struct osl_column
));
274 FOR_EACH_COLUMN(i
, t
->desc
, cd
) {
275 enum osl_storage_type st
= cd
->storage_type
;
276 enum osl_storage_flags sf
= cd
->storage_flags
;
277 struct osl_column
*col
= &t
->columns
[i
];
279 ret
= -E_OSL_BAD_STORAGE_TYPE
;
280 if (st
!= OSL_MAPPED_STORAGE
&& st
!= OSL_DISK_STORAGE
281 && st
!= OSL_NO_STORAGE
)
283 ret
= -E_OSL_BAD_STORAGE_FLAGS
;
284 if (st
== OSL_DISK_STORAGE
&& sf
& OSL_RBTREE
)
286 if ((sf
& OSL_RBTREE
) && !(sf
& OSL_UNIQUE
))
287 WARNING_LOG("invalid storage flags for column %s: "
288 "OSL_RBTREE && !OSL_UNIQUE\n", cd
->name
);
289 ret
= -E_OSL_BAD_STORAGE_SIZE
;
290 if (sf
& OSL_FIXED_SIZE
&& !cd
->data_size
)
293 case OSL_DISK_STORAGE
:
294 t
->num_disk_storage_columns
++;
296 case OSL_MAPPED_STORAGE
:
297 t
->num_mapped_columns
++;
298 col
->index_offset
= t
->row_index_size
;
299 t
->row_index_size
+= 8;
302 col
->volatile_num
= t
->num_volatile_columns
;
303 t
->num_volatile_columns
++;
306 if (sf
& OSL_RBTREE
) {
307 col
->rbtree_num
= t
->num_rbtrees
;
309 if ((sf
& OSL_UNIQUE
) && (st
== OSL_MAPPED_STORAGE
)) {
310 if (!have_disk_storage_name_column
)
311 t
->disk_storage_name_column
= i
;
312 have_disk_storage_name_column
= 1;
316 ret
= -E_OSL_NO_UNIQUE_RBTREE_COLUMN
;
317 if (t
->num_disk_storage_columns
&& !have_disk_storage_name_column
)
319 ret
= -E_OSL_NO_RBTREE_COL
;
323 DEBUG_LOG("OK. Index entry size: %u\n", t
->row_index_size
);
324 ret
= init_column_descriptions(t
);
336 * Read the table description from index header.
338 * \param map The memory mapping of the index file.
339 * \param desc The values found in the index header are returned here.
341 * Read the index header, check for the osl magic string and the table version
342 * number. Read all information stored in the index header into \a desc.
344 * \return The on-disk table version number on success, negative on errors.
346 * \sa struct osl_table_description, osl_create_table.
348 int read_table_desc(struct osl_object
*map
, struct osl_table_description
*desc
)
350 char *buf
= map
->data
;
351 uint8_t table_version
;
352 uint16_t header_size
;
355 struct osl_column_description
*cd
;
357 if (map
->size
< MIN_INDEX_HEADER_SIZE(1))
358 return -E_OSL_SHORT_TABLE
;
359 if (strncmp(buf
+ IDX_OSL_MAGIC
, OSL_MAGIC
, strlen(OSL_MAGIC
)))
360 return -E_OSL_NO_MAGIC
;
361 table_version
= read_u8(buf
+ IDX_VERSION
);
362 INFO_LOG("osl versions (table/min/current): %u/%u/%u\n",
363 table_version
, MIN_TABLE_VERSION
, CURRENT_TABLE_VERSION
);
364 if (table_version
< MIN_TABLE_VERSION
/* table too old */
365 || table_version
> CURRENT_TABLE_VERSION
) /* libosl too old */
366 return -E_OSL_VERSION_MISMATCH
;
367 desc
->flags
= read_u8(buf
+ IDX_TABLE_FLAGS
);
368 desc
->num_columns
= read_u16(buf
+ IDX_NUM_COLUMNS
);
369 INFO_LOG("%u columns\n", desc
->num_columns
);
370 if (!desc
->num_columns
)
371 return -E_OSL_NO_COLUMNS
;
372 header_size
= read_u16(buf
+ IDX_HEADER_SIZE
);
373 if (map
->size
< header_size
)
374 return -E_OSL_BAD_SIZE
;
375 desc
->column_descriptions
= calloc(desc
->num_columns
,
376 sizeof(struct osl_column_description
));
377 if (!desc
->column_descriptions
)
379 offset
= IDX_COLUMN_DESCRIPTIONS
;
380 FOR_EACH_COLUMN(i
, desc
, cd
) {
383 ret
= -E_OSL_SHORT_TABLE
;
384 if (map
->size
< offset
+ MIN_IDX_COLUMN_DESCRIPTION_SIZE
) {
385 ERROR_LOG("map size = %zu < %u = offset + min desc size\n",
386 map
->size
, offset
+ MIN_IDX_COLUMN_DESCRIPTION_SIZE
);
389 cd
->storage_type
= read_u16(buf
+ offset
+ IDX_CD_STORAGE_TYPE
);
390 cd
->storage_flags
= read_u16(buf
+ offset
+
391 IDX_CD_STORAGE_FLAGS
);
392 cd
->data_size
= read_u32(buf
+ offset
+ IDX_CD_DATA_SIZE
);
393 null_byte
= memchr(buf
+ offset
+ IDX_CD_NAME
, '\0',
394 map
->size
- offset
- IDX_CD_NAME
);
395 ret
= -E_OSL_INDEX_CORRUPTION
;
399 cd
->name
= strdup(buf
+ offset
+ IDX_CD_NAME
);
402 offset
+= index_column_description_size(cd
->name
);
404 if (offset
!= header_size
) {
405 ret
= -E_OSL_INDEX_CORRUPTION
;
406 ERROR_LOG("real header size = %u != %u = stored header size\n",
407 offset
, header_size
);
410 return table_version
;
412 FOR_EACH_COLUMN(i
, desc
, cd
)
418 * check whether the table description given by \p t->desc matches the on-disk
419 * table structure stored in the index of \a t.
421 static int compare_table_descriptions(struct osl_table
*t
)
424 struct osl_table_description desc
;
425 const struct osl_column_description
*cd1
, *cd2
;
427 /* read the on-disk structure into desc */
428 ret
= read_table_desc(&t
->index_map
, &desc
);
432 ret
= -E_OSL_BAD_TABLE_FLAGS
;
433 if (desc
.flags
!= t
->desc
->flags
)
435 ret
= -E_OSL_BAD_COLUMN_NUM
;
436 if (desc
.num_columns
> t
->desc
->num_columns
)
438 if (desc
.num_columns
< t
->desc
->num_columns
) {
439 struct osl_column_description
*cd
;
440 unsigned diff
= t
->desc
->num_columns
- desc
.num_columns
;
441 INFO_LOG("extending table by %u volatile columns\n", diff
);
443 desc
.column_descriptions
= realloc(desc
.column_descriptions
,
444 t
->desc
->num_columns
* sizeof(struct osl_column_description
));
445 if (!desc
.column_descriptions
)
447 for (i
= desc
.num_columns
; i
< t
->desc
->num_columns
; i
++) {
448 cd
= get_column_description(&desc
, i
);
449 cd
->storage_type
= OSL_NO_STORAGE
;
452 desc
.num_columns
+= diff
;
454 FOR_EACH_COLUMN(i
, t
->desc
, cd1
) {
455 cd2
= get_column_description(&desc
, i
);
456 ret
= -E_OSL_BAD_STORAGE_TYPE
;
457 if (cd1
->storage_type
!= cd2
->storage_type
)
459 if (cd1
->storage_type
== OSL_NO_STORAGE
)
461 ret
= -E_OSL_BAD_STORAGE_FLAGS
;
462 if (cd1
->storage_flags
!= cd2
->storage_flags
) {
463 ERROR_LOG("sf1 = %u != %u = sf2\n",
464 cd1
->storage_flags
, cd2
->storage_flags
);
467 ret
= -E_OSL_BAD_DATA_SIZE
;
468 if (cd1
->storage_flags
& OSL_FIXED_SIZE
)
469 if (cd1
->data_size
!= cd2
->data_size
)
471 ret
= -E_OSL_BAD_COLUMN_NAME
;
472 if (strcmp(cd1
->name
, cd2
->name
))
475 INFO_LOG("table description of '%s' matches on-disk data, good\n",
479 FOR_EACH_COLUMN(i
, &desc
, cd1
)
481 free(desc
.column_descriptions
);
485 static int create_table_index(struct osl_table
*t
)
487 char *buf
, *filename
;
489 size_t size
= t
->index_header_size
;
490 const struct osl_column_description
*cd
;
493 INFO_LOG("creating %zu byte index for table %s\n", size
,
495 buf
= calloc(1, size
);
498 sprintf(buf
+ IDX_OSL_MAGIC
, "%s", OSL_MAGIC
);
499 write_u8(buf
+ IDX_TABLE_FLAGS
, t
->desc
->flags
);
500 write_u8(buf
+ IDX_DIRTY_FLAG
, 0);
501 write_u8(buf
+ IDX_VERSION
, t
->version
);
502 write_u16(buf
+ IDX_NUM_COLUMNS
, t
->num_mapped_columns
+ t
->num_disk_storage_columns
);
503 write_u16(buf
+ IDX_HEADER_SIZE
, t
->index_header_size
);
504 offset
= IDX_COLUMN_DESCRIPTIONS
;
505 FOR_EACH_COLUMN(i
, t
->desc
, cd
) {
506 /* no need to store info about volatile storage */
507 if (cd
->storage_type
== OSL_NO_STORAGE
)
509 write_u16(buf
+ offset
+ IDX_CD_STORAGE_TYPE
,
511 write_u16(buf
+ offset
+ IDX_CD_STORAGE_FLAGS
,
513 if (cd
->storage_flags
& OSL_FIXED_SIZE
)
514 write_u32(buf
+ offset
+ IDX_CD_DATA_SIZE
,
516 strcpy(buf
+ offset
+ IDX_CD_NAME
, cd
->name
);
517 offset
+= index_column_description_size(cd
->name
);
519 assert(offset
== size
);
520 filename
= index_filename(t
->desc
);
522 ret
= write_file(filename
, buf
, size
);
530 __export
int osl_create_table(const struct osl_table_description
*desc
)
532 const struct osl_column_description
*cd
;
533 char *table_dir
= NULL
, *filename
;
535 int i
, ret
= init_table_structure(desc
, &t
);
539 t
->version
= CURRENT_TABLE_VERSION
;
540 INFO_LOG("creating version %u table %s\n", t
->version
, desc
->name
);
541 FOR_EACH_COLUMN(i
, t
->desc
, cd
) {
542 if (cd
->storage_type
== OSL_NO_STORAGE
)
545 ret
= osl_mkdir(desc
->dir
, 0777);
546 if (ret
< 0 && ret
!= -E_OSL_DIR_EXISTS
)
548 table_dir
= make_message("%s/%s", desc
->dir
,
553 ret
= osl_mkdir(table_dir
, 0777);
557 column_name_hash(t
, cd
->name
, t
->columns
[i
].name_hash
);
559 filename
= column_filename(t
, i
);
562 INFO_LOG("filename: %s\n", filename
);
563 if (cd
->storage_type
== OSL_MAPPED_STORAGE
) {
564 ret
= osl_open(filename
, O_RDWR
| O_CREAT
| O_EXCL
,
573 ret
= osl_mkdir(filename
, 0777);
578 if (t
->num_mapped_columns
) {
579 ret
= create_table_index(t
);
591 static int table_is_dirty(struct osl_table
*t
)
593 char *buf
= (char *)t
->index_map
.data
+ IDX_DIRTY_FLAG
;
594 uint8_t dirty
= read_u8(buf
) & 0x1;
598 static void mark_table_dirty(struct osl_table
*t
)
600 char *buf
= (char *)t
->index_map
.data
+ IDX_DIRTY_FLAG
;
601 write_u8(buf
, read_u8(buf
) | 1);
604 static void mark_table_clean(struct osl_table
*t
)
606 char *buf
= (char *)t
->index_map
.data
+ IDX_DIRTY_FLAG
;
607 write_u8(buf
, read_u8(buf
) & 0xfe);
610 static void unmap_column(struct osl_table
*t
, unsigned col_num
)
612 struct osl_object map
= t
->columns
[col_num
].data_map
;
616 ret
= osl_munmap(map
.data
, map
.size
);
622 * Unmap all mapped files of an osl table.
624 * \param t Pointer to a mapped table.
625 * \param flags Options for unmapping.
627 * \return Positive on success, negative on errors.
629 * \sa map_table(), enum osl_close_flags, osl_munmap().
631 int unmap_table(struct osl_table
*t
, enum osl_close_flags flags
)
634 const struct osl_column_description
*cd
;
637 if (!t
->num_mapped_columns
) /* can this ever happen? */
639 INFO_LOG("unmapping table '%s'\n", t
->desc
->name
);
640 if (!t
->index_map
.data
)
641 return -E_OSL_NOT_MAPPED
;
642 if (flags
& OSL_MARK_CLEAN
)
644 ret
= osl_munmap(t
->index_map
.data
, t
->index_map
.size
);
647 t
->index_map
.data
= NULL
;
650 FOR_EACH_MAPPED_COLUMN(i
, t
, cd
)
655 static int map_column(struct osl_table
*t
, unsigned col_num
)
658 char *filename
= column_filename(t
, col_num
);
663 ret
= osl_stat(filename
, &statbuf
);
668 if (!(S_IFREG
& statbuf
.st_mode
)) {
672 ret
= mmap_full_file(filename
, O_RDWR
,
673 &t
->columns
[col_num
].data_map
.data
,
674 &t
->columns
[col_num
].data_map
.size
,
681 * Map the index file and all columns of type \p OSL_MAPPED_STORAGE into memory.
683 * \param t Pointer to an initialized table structure.
684 * \param flags Mapping options.
686 * \return Negative return value on errors; on success the number of rows
687 * (including invalid rows) is returned.
689 * \sa unmap_table(), enum map_table_flags, osl_open_table(), mmap(2).
691 int map_table(struct osl_table
*t
, enum map_table_flags flags
)
694 const struct osl_column_description
*cd
;
695 int i
= 0, ret
, num_rows
= 0;
697 if (!t
->num_mapped_columns
)
699 if (t
->index_map
.data
)
700 return -E_OSL_ALREADY_MAPPED
;
701 filename
= index_filename(t
->desc
);
704 INFO_LOG("mapping table '%s' (index: %s)\n", t
->desc
->name
, filename
);
705 ret
= mmap_full_file(filename
, flags
& MAP_TBL_FL_MAP_RDONLY
?
706 O_RDONLY
: O_RDWR
, &t
->index_map
.data
, &t
->index_map
.size
, NULL
);
710 if (flags
& MAP_TBL_FL_VERIFY_INDEX
) {
711 ret
= compare_table_descriptions(t
);
716 if (!(flags
& MAP_TBL_FL_IGNORE_DIRTY
)) {
717 if (table_is_dirty(t
)) {
718 ERROR_LOG("%s is dirty\n", t
->desc
->name
);
723 num_rows
= table_num_rows(t
);
725 FOR_EACH_COLUMN(i
, t
->desc
, cd
) {
726 if (cd
->storage_type
== OSL_NO_STORAGE
)
728 column_name_hash(t
, cd
->name
, t
->columns
[i
].name_hash
);
729 if (num_rows
> 0 && cd
->storage_type
== OSL_MAPPED_STORAGE
) {
730 ret
= map_column(t
, i
);
736 err
: /* unmap what is already mapped */
737 for (i
--; i
>= 0; i
--) {
738 struct osl_object map
= t
->columns
[i
].data_map
;
739 osl_munmap(map
.data
, map
.size
);
742 osl_munmap(t
->index_map
.data
, t
->index_map
.size
);
743 t
->index_map
.data
= NULL
;
748 * Retrieve a mapped object by row and column number.
750 * \param t Pointer to an open osl table.
751 * \param col_num Number of the mapped column containing the object to retrieve.
752 * \param row_num Number of the row containing the object to retrieve.
753 * \param obj The result is returned here.
755 * It is considered an error if \a col_num does not refer to a column
756 * of storage type \p OSL_MAPPED_STORAGE.
760 * \sa osl_storage_type.
762 int get_mapped_object(const struct osl_table
*t
, unsigned col_num
,
763 uint32_t row_num
, struct osl_object
*obj
)
765 struct osl_column
*col
= &t
->columns
[col_num
];
770 if (t
->num_rows
<= row_num
)
771 return -E_OSL_BAD_ROW_NUM
;
772 ret
= get_cell_index(t
, row_num
, col_num
, &cell_index
);
775 offset
= read_u32(cell_index
);
776 obj
->size
= read_u32(cell_index
+ 4);
777 obj
->data
= col
->data_map
.data
+ offset
;
782 * It's OK to call this with result = rb_node = NULL. If result is not NULL,
783 * and rb key was not found, result points to the parent node.
785 static int search_rbtree(const struct osl_object
*obj
,
786 const struct osl_table
*t
, unsigned col_num
,
787 struct rb_node
**result
, struct rb_node
***rb_link
)
789 struct osl_column
*col
= &t
->columns
[col_num
];
790 struct rb_node
**new = &col
->rbtree
.rb_node
, *parent
= NULL
;
791 const struct osl_column_description
*cd
=
792 get_column_description(t
->desc
, col_num
);
793 enum osl_storage_type st
= cd
->storage_type
;
795 struct osl_row
*this_row
= get_row_pointer(*new,
798 struct osl_object this_obj
;
800 if (st
== OSL_MAPPED_STORAGE
) {
801 ret
= get_mapped_object(t
, col_num
, this_row
->num
,
806 this_obj
= this_row
->volatile_objects
[col
->volatile_num
];
807 ret
= cd
->compare_function(obj
, &this_obj
);
810 *result
= get_rb_node_pointer(this_row
,
815 new = &((*new)->rb_left
);
817 new = &((*new)->rb_right
);
823 return -E_OSL_RB_KEY_NOT_FOUND
;
826 static int insert_rbtree(struct osl_table
*t
, unsigned col_num
,
827 const struct osl_row
*row
, const struct osl_object
*obj
)
829 struct rb_node
*parent
, **rb_link
;
832 int ret
= search_rbtree(obj
, t
, col_num
, &parent
, &rb_link
);
835 return -E_OSL_RB_KEY_EXISTS
;
836 rbtree_num
= t
->columns
[col_num
].rbtree_num
;
837 n
= get_rb_node_pointer(row
, rbtree_num
);
838 rb_link_node(n
, parent
, rb_link
);
839 rb_insert_color(n
, &t
->columns
[col_num
].rbtree
);
843 static void remove_rb_node(struct osl_table
*t
, unsigned col_num
,
844 const struct osl_row
*row
)
846 struct osl_column
*col
= &t
->columns
[col_num
];
847 const struct osl_column_description
*cd
=
848 get_column_description(t
->desc
, col_num
);
849 enum osl_storage_flags sf
= cd
->storage_flags
;
850 struct rb_node
*victim
, *splice_out_node
, *tmp
;
851 if (!(sf
& OSL_RBTREE
))
854 * Which node is removed/spliced out actually depends on how many
855 * children the victim node has: If it has no children, it gets
856 * deleted. If it has one child, it gets spliced out. If it has two
857 * children, its successor (which has at most a right child) gets
860 victim
= get_rb_node_pointer(row
, col
->rbtree_num
);
861 if (victim
->rb_left
&& victim
->rb_right
)
862 splice_out_node
= rb_next(victim
);
864 splice_out_node
= victim
;
865 /* Go up to the root and decrement the size of each node in the path. */
866 for (tmp
= splice_out_node
; tmp
; tmp
= rb_parent(tmp
))
868 rb_erase(victim
, &col
->rbtree
);
871 static int add_row_to_rbtrees(struct osl_table
*t
, uint32_t row_num
,
872 struct osl_object
*volatile_objs
, struct osl_row
**row_ptr
)
876 struct osl_row
*row
= allocate_row(t
->num_rbtrees
);
877 const struct osl_column_description
*cd
;
882 row
->volatile_objects
= volatile_objs
;
883 FOR_EACH_RBTREE_COLUMN(i
, t
, cd
) {
884 if (cd
->storage_type
== OSL_MAPPED_STORAGE
) {
885 struct osl_object obj
;
886 ret
= get_mapped_object(t
, i
, row_num
, &obj
);
889 ret
= insert_rbtree(t
, i
, row
, &obj
);
890 } else { /* volatile */
891 const struct osl_object
*obj
892 = volatile_objs
+ t
->columns
[i
].volatile_num
;
893 ret
= insert_rbtree(t
, i
, row
, obj
);
901 err
: /* rollback changes, i.e. remove added entries from rbtrees */
903 remove_rb_node(t
, i
--, row
);
908 static void free_volatile_objects(const struct osl_table
*t
,
909 enum osl_close_flags flags
)
913 struct osl_column
*rb_col
;
914 const struct osl_column_description
*cd
;
916 if (!t
->num_volatile_columns
)
918 /* find the first rbtree column (any will do) */
919 FOR_EACH_RBTREE_COLUMN(i
, t
, cd
)
921 rb_col
= t
->columns
+ i
;
922 /* walk that rbtree and free all volatile objects */
923 for (n
= rb_first(&rb_col
->rbtree
); n
; n
= rb_next(n
)) {
924 struct osl_row
*r
= get_row_pointer(n
, rb_col
->rbtree_num
);
925 if (flags
& OSL_FREE_VOLATILE
)
926 FOR_EACH_VOLATILE_COLUMN(j
, t
, cd
) {
927 if (cd
->storage_flags
& OSL_DONT_FREE
)
929 free(r
->volatile_objects
[
930 t
->columns
[j
].volatile_num
].data
);
932 // for (j = 0; j < t->num_volatile_columns; j++)
933 // free(r->volatile_objects[j].data);
934 free(r
->volatile_objects
);
939 * Erase all rbtree nodes and free resources.
941 * \param t Pointer to an open osl table.
943 * This function is called by osl_close_table().
945 void clear_rbtrees(struct osl_table
*t
)
947 const struct osl_column_description
*cd
;
948 unsigned i
, rbtrees_cleared
= 0;
950 FOR_EACH_RBTREE_COLUMN(i
, t
, cd
) {
951 struct osl_column
*col
= &t
->columns
[i
];
954 for (n
= rb_first(&col
->rbtree
); n
;) {
956 rb_erase(n
, &col
->rbtree
);
957 if (rbtrees_cleared
== t
->num_rbtrees
) {
958 r
= get_row_pointer(n
, col
->rbtree_num
);
968 __export
int osl_close_table(struct osl_table
*t
, enum osl_close_flags flags
)
973 return -E_OSL_BAD_TABLE
;
974 NOTICE_LOG("closing table %s\n", t
->desc
->name
);
975 free_volatile_objects(t
, flags
);
977 ret
= unmap_table(t
, flags
);
979 ERROR_LOG("unmap_table failed: %d\n", ret
);
986 * Find out whether the given row number corresponds to an invalid row.
988 * \param t Pointer to the osl table.
989 * \param row_num The number of the row in question.
991 * By definition, a row is considered invalid if all its index entries
994 * \return Positive if \a row_num corresponds to an invalid row,
995 * zero if it corresponds to a valid row, negative on errors.
997 int row_is_invalid(struct osl_table
*t
, uint32_t row_num
)
1000 int ret
= get_row_index(t
, row_num
, &row_index
);
1005 for (n
= 0; n
< t
->row_index_size
; n
++) {
1006 if ((unsigned char)row_index
[n
] != 0xff)
1009 INFO_LOG("row %d is invalid\n", row_num
);
1014 * Invalidate a row of an osl table.
1016 * \param t Pointer to an open osl table.
1017 * \param row_num Number of the row to mark as invalid.
1019 * This function marks each mapped object in the index entry of \a row as
1024 int mark_row_invalid(struct osl_table
*t
, uint32_t row_num
)
1027 int ret
= get_row_index(t
, row_num
, &row_index
);
1031 INFO_LOG("marking row %d as invalid\n", row_num
);
1032 memset(row_index
, 0xff, t
->row_index_size
);
1037 * Initialize all rbtrees and compute number of invalid rows.
1039 * \param t The table containing the rbtrees to be initialized.
1043 int init_rbtrees(struct osl_table
*t
)
1047 const struct osl_column_description
*cd
;
1049 /* create rbtrees */
1050 FOR_EACH_RBTREE_COLUMN(n
, t
, cd
)
1051 t
->columns
[n
].rbtree
= RB_ROOT
;
1052 /* add valid rows to rbtrees */
1053 t
->num_invalid_rows
= 0;
1054 for (n
= 0; n
< t
->num_rows
; n
++) {
1055 struct osl_object
*volatile_objs
;
1056 ret
= row_is_invalid(t
, n
);
1060 t
->num_invalid_rows
++;
1063 if (t
->num_volatile_columns
> 0) {
1064 volatile_objs
= calloc(t
->num_volatile_columns
,
1065 sizeof(struct osl_object
));
1067 return -E_OSL_NOMEM
;
1069 volatile_objs
= NULL
;
1070 ret
= add_row_to_rbtrees(t
, n
, volatile_objs
, NULL
);
1077 __export
int osl_open_table(const struct osl_table_description
*table_desc
,
1078 struct osl_table
**result
)
1081 struct osl_table
*t
;
1083 NOTICE_LOG("opening table %s\n", table_desc
->name
);
1084 ret
= init_table_structure(table_desc
, &t
);
1087 ret
= map_table(t
, MAP_TBL_FL_VERIFY_INDEX
);
1091 DEBUG_LOG("num rows: %d\n", t
->num_rows
);
1092 ret
= init_rbtrees(t
);
1094 osl_close_table(t
, OSL_MARK_CLEAN
); /* ignore further errors */
1105 static int create_disk_storage_object_dir(const struct osl_table
*t
,
1106 unsigned col_num
, const char *ds_name
)
1111 if (!(t
->desc
->flags
& OSL_LARGE_TABLE
))
1113 dirname
= disk_storage_dirname(t
, col_num
, ds_name
);
1115 return -E_OSL_NOMEM
;
1116 ret
= osl_mkdir(dirname
, 0777);
1118 if (ret
< 0 && ret
!= -E_OSL_DIR_EXISTS
)
1123 static int write_disk_storage_file(const struct osl_table
*t
, unsigned col_num
,
1124 const struct osl_object
*obj
, const char *ds_name
)
1129 ret
= create_disk_storage_object_dir(t
, col_num
, ds_name
);
1132 filename
= disk_storage_path(t
, col_num
, ds_name
);
1134 return -E_OSL_NOMEM
;
1135 ret
= write_file(filename
, obj
->data
, obj
->size
);
1140 static int append_map_file(const struct osl_table
*t
, unsigned col_num
,
1141 const struct osl_object
*obj
, uint32_t *new_size
)
1143 char *filename
= column_filename(t
, col_num
);
1147 return -E_OSL_NOMEM
;
1148 ret
= append_file(filename
, obj
->data
, obj
->size
, new_size
);
1153 static int append_row_index(const struct osl_table
*t
, char *row_index
)
1158 if (!t
->num_mapped_columns
)
1160 filename
= index_filename(t
->desc
);
1162 return -E_OSL_NOMEM
;
1163 ret
= append_file(filename
, row_index
, t
->row_index_size
, NULL
);
1168 static int truncate_mapped_file(const struct osl_table
*t
, unsigned col_num
,
1172 char *filename
= column_filename(t
, col_num
);
1175 return -E_OSL_NOMEM
;
1176 ret
= truncate_file(filename
, size
);
1181 static int delete_disk_storage_file(const struct osl_table
*t
, unsigned col_num
,
1182 const char *ds_name
)
1184 char *dirname
, *filename
= disk_storage_path(t
, col_num
, ds_name
);
1188 return -E_OSL_NOMEM
;
1189 if (unlink(filename
) < 0)
1190 ret
= errno
== ENOENT
? -E_OSL_NOENT
: -E_OSL_UNLINK
;
1194 if (!(t
->desc
->flags
& OSL_LARGE_TABLE
))
1196 dirname
= disk_storage_dirname(t
, col_num
, ds_name
);
1198 return -E_OSL_NOMEM
;
1204 __export
int osl_add_and_get_row(struct osl_table
*t
, struct osl_object
*objects
,
1205 struct osl_row
**row
)
1208 char *ds_name
= NULL
;
1209 struct rb_node
**rb_parents
= NULL
, ***rb_links
= NULL
;
1210 char *new_row_index
= NULL
;
1211 struct osl_object
*volatile_objs
= NULL
;
1212 const struct osl_column_description
*cd
;
1215 return -E_OSL_BAD_TABLE
;
1216 rb_parents
= malloc(t
->num_rbtrees
* sizeof(struct rn_node
*));
1218 return -E_OSL_NOMEM
;
1219 rb_links
= malloc(t
->num_rbtrees
* sizeof(struct rn_node
**));
1222 return -E_OSL_NOMEM
;
1224 if (t
->num_mapped_columns
) {
1225 new_row_index
= malloc(t
->row_index_size
);
1226 if (!new_row_index
) {
1229 return -E_OSL_NOMEM
;
1232 /* pass 1: sanity checks */
1233 // DEBUG_LOG("sanity tests: %p:%p\n", objects[0].data,
1234 // objects[1].data);
1235 FOR_EACH_COLUMN(i
, t
->desc
, cd
) {
1236 enum osl_storage_type st
= cd
->storage_type
;
1237 enum osl_storage_flags sf
= cd
->storage_flags
;
1239 // ret = -E_OSL_NULL_OBJECT;
1242 if (st
== OSL_DISK_STORAGE
)
1244 if (sf
& OSL_RBTREE
) {
1245 unsigned rbtree_num
= t
->columns
[i
].rbtree_num
;
1246 ret
= -E_OSL_RB_KEY_EXISTS
;
1247 // DEBUG_LOG("checking whether %p exists\n",
1248 // objects[i].data);
1249 if (search_rbtree(objects
+ i
, t
, i
,
1250 &rb_parents
[rbtree_num
],
1251 &rb_links
[rbtree_num
]) > 0)
1254 if (sf
& OSL_FIXED_SIZE
) {
1255 // DEBUG_LOG("fixed size. need: %zu, have: %d\n",
1256 // objects[i].size, cd->data_size);
1257 ret
= -E_OSL_BAD_DATA_SIZE
;
1258 if (objects
[i
].size
!= cd
->data_size
)
1262 if (t
->num_disk_storage_columns
) {
1263 ds_name
= disk_storage_name_of_object(t
,
1264 &objects
[t
->disk_storage_name_column
]);
1269 ret
= unmap_table(t
, OSL_MARK_CLEAN
);
1272 // DEBUG_LOG("sanity tests passed%s\n", "");
1273 /* pass 2: create data files, append map data */
1274 FOR_EACH_COLUMN(i
, t
->desc
, cd
) {
1275 enum osl_storage_type st
= cd
->storage_type
;
1276 if (st
== OSL_NO_STORAGE
)
1278 if (st
== OSL_MAPPED_STORAGE
) {
1280 struct osl_column
*col
= &t
->columns
[i
];
1281 // DEBUG_LOG("appending object of size %zu\n",
1282 // objects[i].size);
1283 ret
= append_map_file(t
, i
, objects
+ i
, &new_size
);
1286 update_cell_index(new_row_index
, col
, new_size
,
1291 ret
= write_disk_storage_file(t
, i
, objects
+ i
, ds_name
);
1295 ret
= append_row_index(t
, new_row_index
);
1298 ret
= map_table(t
, MAP_TBL_FL_VERIFY_INDEX
);
1299 if (ret
< 0) { /* truncate index and rollback changes */
1300 char *filename
= index_filename(t
->desc
);
1302 truncate_file(filename
, t
->row_index_size
);
1306 /* pass 3: add entry to rbtrees */
1307 if (t
->num_volatile_columns
) {
1309 volatile_objs
= calloc(t
->num_volatile_columns
,
1310 sizeof(struct osl_object
));
1313 FOR_EACH_VOLATILE_COLUMN(i
, t
, cd
)
1314 volatile_objs
[t
->columns
[i
].volatile_num
] = objects
[i
];
1317 // DEBUG_LOG("adding new entry as row #%d\n", t->num_rows - 1);
1318 ret
= add_row_to_rbtrees(t
, t
->num_rows
- 1, volatile_objs
, row
);
1321 // DEBUG_LOG("added new entry as row #%d\n", t->num_rows - 1);
1324 rollback
: /* rollback all changes made, ignore further errors */
1325 for (i
--; i
>= 0; i
--) {
1326 cd
= get_column_description(t
->desc
, i
);
1327 enum osl_storage_type st
= cd
->storage_type
;
1328 if (st
== OSL_NO_STORAGE
)
1331 if (st
== OSL_MAPPED_STORAGE
)
1332 truncate_mapped_file(t
, i
, objects
[i
].size
);
1333 else /* disk storage */
1334 delete_disk_storage_file(t
, i
, ds_name
);
1336 /* ignore error and return previous error value */
1337 map_table(t
, MAP_TBL_FL_VERIFY_INDEX
);
1339 free(new_row_index
);
1346 __export
int osl_add_row(struct osl_table
*t
, struct osl_object
*objects
)
1348 return osl_add_and_get_row(t
, objects
, NULL
);
1351 __export
int osl_get_object(const struct osl_table
*t
, const struct osl_row
*r
,
1352 unsigned col_num
, struct osl_object
*object
)
1354 const struct osl_column_description
*cd
;
1357 return -E_OSL_BAD_TABLE
;
1358 cd
= get_column_description(t
->desc
, col_num
);
1359 /* col must not be disk storage */
1360 if (cd
->storage_type
== OSL_DISK_STORAGE
)
1361 return -E_OSL_BAD_STORAGE_TYPE
;
1362 if (cd
->storage_type
== OSL_MAPPED_STORAGE
)
1363 return get_mapped_object(t
, col_num
, r
->num
, object
);
1365 *object
= r
->volatile_objects
[t
->columns
[col_num
].volatile_num
];
1369 __export
int osl_del_row(struct osl_table
*t
, struct osl_row
*row
)
1371 struct osl_row
*r
= row
;
1373 const struct osl_column_description
*cd
;
1376 return -E_OSL_BAD_TABLE
;
1377 INFO_LOG("deleting row %p\n", row
);
1379 if (t
->num_disk_storage_columns
) {
1381 ret
= disk_storage_name_of_row(t
, r
, &ds_name
);
1384 FOR_EACH_DISK_STORAGE_COLUMN(i
, t
, cd
)
1385 delete_disk_storage_file(t
, i
, ds_name
);
1388 FOR_EACH_COLUMN(i
, t
->desc
, cd
) {
1389 struct osl_column
*col
= t
->columns
+ i
;
1390 enum osl_storage_type st
= cd
->storage_type
;
1391 remove_rb_node(t
, i
, r
);
1392 if (st
== OSL_MAPPED_STORAGE
)
1394 if (st
== OSL_NO_STORAGE
&& !(cd
->storage_flags
& OSL_DONT_FREE
))
1395 free(r
->volatile_objects
[col
->volatile_num
].data
);
1397 if (t
->num_mapped_columns
) {
1398 ret
= mark_row_invalid(t
, r
->num
);
1401 t
->num_invalid_rows
++;
1406 free(r
->volatile_objects
);
1411 /* test if column has an rbtree */
1412 static int check_rbtree_col(const struct osl_table
*t
, unsigned col_num
,
1413 struct osl_column
**col
)
1416 return -E_OSL_BAD_TABLE
;
1417 if (!(get_column_description(t
->desc
, col_num
)->storage_flags
& OSL_RBTREE
))
1418 return -E_OSL_BAD_STORAGE_FLAGS
;
1419 *col
= t
->columns
+ col_num
;
1423 __export
int osl_get_row(const struct osl_table
*t
, unsigned col_num
,
1424 const struct osl_object
*obj
, struct osl_row
**result
)
1427 struct rb_node
*node
;
1428 struct osl_row
*row
;
1429 struct osl_column
*col
;
1432 ret
= check_rbtree_col(t
, col_num
, &col
);
1435 ret
= search_rbtree(obj
, t
, col_num
, &node
, NULL
);
1438 row
= get_row_pointer(node
, t
->columns
[col_num
].rbtree_num
);
1443 static int rbtree_loop(struct osl_column
*col
, void *private_data
,
1444 osl_rbtree_loop_func
*func
)
1446 struct rb_node
*n
, *tmp
;
1448 /* this for-loop is safe against removal of an entry */
1449 for (n
= rb_first(&col
->rbtree
), tmp
= n
? rb_next(n
) : NULL
;
1451 n
= tmp
, tmp
= tmp
? rb_next(tmp
) : NULL
) {
1452 struct osl_row
*r
= get_row_pointer(n
, col
->rbtree_num
);
1453 if (func(r
, private_data
) < 0)
1459 static int rbtree_loop_reverse(struct osl_column
*col
, void *private_data
,
1460 osl_rbtree_loop_func
*func
)
1462 struct rb_node
*n
, *tmp
;
1464 /* safe against removal of an entry */
1465 for (n
= rb_last(&col
->rbtree
), tmp
= n
? rb_prev(n
) : NULL
;
1467 n
= tmp
, tmp
= tmp
? rb_prev(tmp
) : NULL
) {
1468 struct osl_row
*r
= get_row_pointer(n
, col
->rbtree_num
);
1469 if (func(r
, private_data
) < 0)
1475 __export
int osl_rbtree_loop(const struct osl_table
*t
, unsigned col_num
,
1476 void *private_data
, osl_rbtree_loop_func
*func
)
1478 struct osl_column
*col
;
1480 int ret
= check_rbtree_col(t
, col_num
, &col
);
1483 return rbtree_loop(col
, private_data
, func
);
1486 __export
int osl_rbtree_loop_reverse(const struct osl_table
*t
, unsigned col_num
,
1487 void *private_data
, osl_rbtree_loop_func
*func
)
1489 struct osl_column
*col
;
1491 int ret
= check_rbtree_col(t
, col_num
, &col
);
1494 return rbtree_loop_reverse(col
, private_data
, func
);
1497 /* TODO: Rollback changes on errors */
1498 static int rename_disk_storage_objects(struct osl_table
*t
,
1499 struct osl_object
*old_obj
, struct osl_object
*new_obj
)
1502 const struct osl_column_description
*cd
;
1503 char *old_ds_name
, *new_ds_name
;
1505 if (!t
->num_disk_storage_columns
)
1506 return 1; /* nothing to do */
1507 if (old_obj
->size
== new_obj
->size
&& !memcmp(new_obj
->data
,
1508 old_obj
->data
, new_obj
->size
))
1509 return 1; /* object did not change */
1510 old_ds_name
= disk_storage_name_of_object(t
, old_obj
);
1511 new_ds_name
= disk_storage_name_of_object(t
, new_obj
);
1513 if (!old_ds_name
|| ! new_ds_name
)
1516 FOR_EACH_DISK_STORAGE_COLUMN(i
, t
, cd
) {
1517 char *old_filename
, *new_filename
;
1518 ret
= create_disk_storage_object_dir(t
, i
, new_ds_name
);
1521 old_filename
= disk_storage_path(t
, i
, old_ds_name
);
1522 new_filename
= disk_storage_path(t
, i
, new_ds_name
);
1523 if (!old_filename
|| !new_filename
)
1526 ret
= osl_rename(old_filename
, new_filename
);
1540 __export
int osl_update_object(struct osl_table
*t
, const struct osl_row
*r
,
1541 unsigned col_num
, struct osl_object
*obj
)
1543 struct osl_column
*col
;
1544 const struct osl_column_description
*cd
;
1548 return -E_OSL_BAD_TABLE
;
1549 col
= &t
->columns
[col_num
];
1550 cd
= get_column_description(t
->desc
, col_num
);
1551 DEBUG_LOG("updating column %u of %s\n", col_num
, t
->desc
->name
);
1552 if (cd
->storage_flags
& OSL_RBTREE
) {
1553 if (search_rbtree(obj
, t
, col_num
, NULL
, NULL
) > 0)
1554 return -E_OSL_RB_KEY_EXISTS
;
1556 if (cd
->storage_flags
& OSL_FIXED_SIZE
) {
1557 if (obj
->size
!= cd
->data_size
)
1558 return -E_OSL_BAD_DATA_SIZE
;
1560 remove_rb_node(t
, col_num
, r
);
1561 if (cd
->storage_type
== OSL_NO_STORAGE
) { /* TODO: If fixed size, reuse object? */
1562 if (!(cd
->storage_flags
& OSL_DONT_FREE
))
1563 free(r
->volatile_objects
[col
->volatile_num
].data
);
1564 r
->volatile_objects
[col
->volatile_num
] = *obj
;
1565 } else if (cd
->storage_type
== OSL_DISK_STORAGE
) {
1567 ret
= disk_storage_name_of_row(t
, r
, &ds_name
);
1570 ret
= delete_disk_storage_file(t
, col_num
, ds_name
);
1571 if (ret
< 0 && ret
!= -E_OSL_NOENT
) {
1575 ret
= write_disk_storage_file(t
, col_num
, obj
, ds_name
);
1579 } else { /* mapped storage */
1580 struct osl_object old_obj
;
1581 ret
= get_mapped_object(t
, col_num
, r
->num
, &old_obj
);
1585 * If the updated column is the disk storage name column, the
1586 * disk storage name changes, so we have to rename all disk
1587 * storage objects accordingly.
1589 if (col_num
== t
->disk_storage_name_column
) {
1590 ret
= rename_disk_storage_objects(t
, &old_obj
, obj
);
1594 if (cd
->storage_flags
& OSL_FIXED_SIZE
)
1595 memcpy(old_obj
.data
, obj
->data
, cd
->data_size
);
1596 else { /* TODO: if the size doesn't change, use old space */
1597 uint32_t new_data_map_size
;
1599 ret
= get_row_index(t
, r
->num
, &row_index
);
1602 unmap_column(t
, col_num
);
1603 ret
= append_map_file(t
, col_num
, obj
,
1604 &new_data_map_size
);
1607 ret
= map_column(t
, col_num
);
1610 update_cell_index(row_index
, col
, new_data_map_size
,
1614 if (cd
->storage_flags
& OSL_RBTREE
) {
1615 ret
= insert_rbtree(t
, col_num
, r
, obj
);
1622 __export
int osl_open_disk_object(const struct osl_table
*t
, const struct osl_row
*r
,
1623 unsigned col_num
, struct osl_object
*obj
)
1625 const struct osl_column_description
*cd
;
1626 char *ds_name
, *filename
;
1630 return -E_OSL_BAD_TABLE
;
1631 cd
= get_column_description(t
->desc
, col_num
);
1632 if (cd
->storage_type
!= OSL_DISK_STORAGE
)
1633 return -E_OSL_BAD_STORAGE_TYPE
;
1635 ret
= disk_storage_name_of_row(t
, r
, &ds_name
);
1638 filename
= disk_storage_path(t
, col_num
, ds_name
);
1641 return -E_OSL_NOMEM
;
1642 DEBUG_LOG("filename: %s\n", filename
);
1643 ret
= mmap_full_file(filename
, O_RDONLY
, &obj
->data
, &obj
->size
, NULL
);
1648 __export
int osl_close_disk_object(struct osl_object
*obj
)
1650 return osl_munmap(obj
->data
, obj
->size
);
1653 __export
int osl_get_num_rows(const struct osl_table
*t
, unsigned *num_rows
)
1656 return -E_OSL_BAD_TABLE
;
1657 assert(t
->num_rows
>= t
->num_invalid_rows
);
1658 *num_rows
= t
->num_rows
- t
->num_invalid_rows
;
1662 __export
int osl_get_rank(const struct osl_table
*t
, struct osl_row
*r
,
1663 unsigned col_num
, unsigned *rank
)
1665 struct osl_object obj
;
1666 struct osl_column
*col
;
1667 struct rb_node
*node
;
1668 int ret
= check_rbtree_col(t
, col_num
, &col
);
1672 ret
= osl_get_object(t
, r
, col_num
, &obj
);
1675 ret
= search_rbtree(&obj
, t
, col_num
, &node
, NULL
);
1678 ret
= rb_rank(node
, rank
);
1680 return -E_OSL_BAD_ROW
;
1684 __export
int osl_get_nth_row(const struct osl_table
*t
, unsigned col_num
,
1685 unsigned n
, struct osl_row
**result
)
1687 struct osl_column
*col
;
1688 struct rb_node
*node
;
1694 return -E_OSL_RB_KEY_NOT_FOUND
;
1695 ret
= osl_get_num_rows(t
, &num_rows
);
1699 return -E_OSL_RB_KEY_NOT_FOUND
;
1700 ret
= check_rbtree_col(t
, col_num
, &col
);
1703 node
= rb_nth(col
->rbtree
.rb_node
, n
);
1705 return -E_OSL_RB_KEY_NOT_FOUND
;
1706 *result
= get_row_pointer(node
, col
->rbtree_num
);
1710 __export
int osl_rbtree_first_row(const struct osl_table
*t
, unsigned col_num
,
1711 struct osl_row
**result
)
1713 return osl_get_nth_row(t
, col_num
, 1, result
);
1716 __export
int osl_rbtree_last_row(const struct osl_table
*t
, unsigned col_num
,
1717 struct osl_row
**result
)
1720 int ret
= osl_get_num_rows(t
, &num_rows
);
1724 return osl_get_nth_row(t
, col_num
, num_rows
, result
);