]> uap-core.de Git - note.git/commitdiff
remove collection type/table main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 1 May 2025 14:01:36 +0000 (16:01 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 1 May 2025 14:01:36 +0000 (16:01 +0200)
20 files changed:
application/application.h
application/attachment.c
application/note.c
application/notebook.c
application/notebook.h
application/store.c
application/store.h
application/store_sqlite.c
application/types.c
application/types.h
application/window.c
ucx/buffer.c
ucx/cx/buffer.h
ucx/cx/common.h
ucx/cx/list.h
ucx/cx/string.h
ucx/cx/tree.h
ucx/list.c
ucx/string.c
ucx/tree.c

index 345d749820086aead75df14ef67a64231bab3329..a70fab80723e256dc70241ae052b917b761c6c16 100644 (file)
@@ -48,8 +48,8 @@ extern "C" {
 // typedefs for NotebookModel and NoteModel are in types.h
     
 typedef struct NavStack {
 // typedefs for NotebookModel and NoteModel are in types.h
     
 typedef struct NavStack {
-    int64_t collection_id;
-    int64_t note_id;
+    int64_t collection_resource_id;
+    int64_t resource_id;
     uint64_t view_flags;
 } NavStack;
     
     uint64_t view_flags;
 } NavStack;
     
@@ -74,7 +74,7 @@ typedef struct MainWindow {
     NotebookModel *current_notebook;
     
     /*
     NotebookModel *current_notebook;
     
     /*
-     * key: collection_id
+     * key: resource_id
      * value: NotebookModel*
      */
     CxMap *notebook_cache;
      * value: NotebookModel*
      */
     CxMap *notebook_cache;
@@ -99,13 +99,13 @@ struct NotebookModel {
      * 
      * After a NoteStore refresh, this pointer is invalid and must be renewed.
      */
      * 
      * After a NoteStore refresh, this pointer is invalid and must be renewed.
      */
-    Collection *collection;
+    Resource *collection;
     
     /*
     
     /*
-     * same as collection->collection_id, however Collection* may be an invalid
-     * pointer, therefore the collection_id is required separately.
+     * same as collection->resource_id, however Resource* may be an invalid
+     * pointer, therefore the resource_id is required separately.
      */
      */
-    int64_t collection_id;
+    int64_t resource_id;
     
     /*
      * list of Note*
     
     /*
      * list of Note*
index 2fb2725dc311243716c0cfa2421fe8639979fc22..be271fd8e57259e30e319eb40f9a2a2d2b38dfb7 100644 (file)
@@ -92,7 +92,7 @@ int attachment_set_image_from_data(Attachment *attachment, cxmutstr data) {
 }
 
 void attachment_set_data(Attachment *attachment, cxmutstr data) {
 }
 
 void attachment_set_data(Attachment *attachment, cxmutstr data) {
-    attachment->bin_content = data;
+    attachment->content = data;
 }
 
 static void attachment_save_result(UiEvent *event, int error, void *userdata) {
 }
 
 static void attachment_save_result(UiEvent *event, int error, void *userdata) {
@@ -116,9 +116,9 @@ void attachment_save(UiObject *obj, Attachment *attachment, bool cleanup_content
         note_store_save_attachment_async(obj, attachment, attachment_save_result, attachment);
         attachment->save_in_progress = FALSE;
         if(cleanup_content) {
         note_store_save_attachment_async(obj, attachment, attachment_save_result, attachment);
         attachment->save_in_progress = FALSE;
         if(cleanup_content) {
-            cxFree(attachment->ui->note_allocator, attachment->bin_content.ptr);
-            attachment->bin_content.ptr = NULL;
-            attachment->bin_content.length = 0;
+            cxFree(attachment->ui->note_allocator, attachment->content.ptr);
+            attachment->content.ptr = NULL;
+            attachment->content.length = 0;
         }
     }
 }
         }
     }
 }
index 6ba7d3c63442d6311f253399e3841739ba3d7f33..c1d1bf495cf30c88fdfc3e17a59d1ddb14a80ae9 100644 (file)
@@ -90,7 +90,7 @@ NoteModel* notemodel_create(const CxAllocator *note_allocator) {
 void notemodel_set_note(NoteModel *model, Resource *note) {
     note->model = model;
     
 void notemodel_set_note(NoteModel *model, Resource *note) {
     note->model = model;
     
-    ui_set(model->title, note->title);
+    ui_set(model->title, note->displayname);
     
     if(note->content_loaded) {
         // TODO: when multiple note types are implemented, check contenttype
     
     if(note->content_loaded) {
         // TODO: when multiple note types are implemented, check contenttype
@@ -117,7 +117,7 @@ static void note_loading_completed(UiObject *obj, LoadNoteContent *op) {
         CxIterator i = cxListIterator(note->attachments);
         cx_foreach(Attachment *, attachment, i) {
             ui_list_append(note->model->attachments, attachment);
         CxIterator i = cxListIterator(note->attachments);
         cx_foreach(Attachment *, attachment, i) {
             ui_list_append(note->model->attachments, attachment);
-            if(attachment_set_image_from_data(attachment, attachment->bin_content)) {
+            if(attachment_set_image_from_data(attachment, attachment->content)) {
                 fprintf(stderr, "Error: cannot open attachment image data\n");
             }
         }
                 fprintf(stderr, "Error: cannot open attachment image data\n");
             }
         }
@@ -150,7 +150,7 @@ static void note_attachments_loaded(UiEvent *event, int error, void *userdata) {
         CxIterator i = cxListIterator(op->note->attachments);
         cx_foreach(Attachment *, attachment, i) {
             attachment_create_ui_model(op->note->model->ctx, attachment, op->note);
         CxIterator i = cxListIterator(op->note->attachments);
         cx_foreach(Attachment *, attachment, i) {
             attachment_create_ui_model(op->note->model->ctx, attachment, op->note);
-            if(attachment->bin_content.length > 0) {
+            if(attachment->content.length > 0) {
                 printf("attachment content loaded\n");
                 attachment->saved = TRUE;
             } // else: TODO: is this an error?
                 printf("attachment content loaded\n");
                 attachment->saved = TRUE;
             } // else: TODO: is this an error?
@@ -185,11 +185,11 @@ void note_save(UiObject *obj, NotebookModel *notebook, Resource *note) {
     char *title = ui_get(m->title);
     const CxAllocator *a = notebook->current_notes_pool->allocator;
     
     char *title = ui_get(m->title);
     const CxAllocator *a = notebook->current_notes_pool->allocator;
     
-    cxFree(a, note->title);
-    note->title = cx_strdup_a(a, cx_str(title)).ptr;
+    cxFree(a, note->displayname);
+    note->displayname = cx_strdup_a(a, cx_str(title)).ptr;
     
     
-    cxFree(a, note->name);
-    note->name = cx_strdup_a(a, cx_str(title)).ptr;
+    cxFree(a, note->nodename);
+    note->nodename = cx_strdup_a(a, cx_str(title)).ptr;
     
     cxmutstr content = editor_get_markdown(m->text, a);
     cxFree(a, note->content.ptr);
     
     cxmutstr content = editor_get_markdown(m->text, a);
     cxFree(a, note->content.ptr);
@@ -271,25 +271,21 @@ void note_update_title(NotebookModel *notebook, Resource *note) {
     m->modified = TRUE;
     
     char *title = ui_get(m->title);
     m->modified = TRUE;
     
     char *title = ui_get(m->title);
-    cxFree(m->note_allocator, note->title);
-    note->title = cx_strdup_a(m->note_allocator, cx_str(title)).ptr;
+    cxFree(m->note_allocator, note->displayname);
+    note->displayname = cx_strdup_a(m->note_allocator, cx_str(title)).ptr;
     
     notebook->notes->update(notebook->notes, index);
 }
 
 const char* note_get_title(Resource *note) {
     
     notebook->notes->update(notebook->notes, index);
 }
 
 const char* note_get_title(Resource *note) {
-    return note->title ? note->title : note->name;
+    return note->displayname ? note->displayname : note->nodename;
 }
 
 void note_destroy(const CxAllocator *a, Resource *note) {
 }
 
 void note_destroy(const CxAllocator *a, Resource *note) {
-    cxFree(a, note->name);
-    cxFree(a, note->title);
-    cxFree(a, note->lastmodified);
-    cxFree(a, note->creationdate);
+    cxFree(a, note->nodename);
+    cxFree(a, note->displayname);
     cxFree(a, note->contenttype);
     cxFree(a, note->contenttype);
-    cxFree(a, note->created_by);
     cxFree(a, note->content.ptr);
     cxFree(a, note->content.ptr);
-    cxFree(a, note->bin_content.ptr);
     
     if(note->model) {
         // TODO: destroy model->context
     
     if(note->model) {
         // TODO: destroy model->context
index a3f47e9992d9ce09d437b259285b2afaeb503f81..14140daeb0010f26b97dfcdf6706b18b790d5f30 100644 (file)
@@ -89,9 +89,9 @@ void notebookmodel_detach(NotebookModel *model) {
     }
 }
 
     }
 }
 
-void notebookmodel_set_collection(NotebookModel *model, Collection *collection) {
+void notebookmodel_set_collection(NotebookModel *model, Resource *collection) {
     model->collection = collection;
     model->collection = collection;
-    model->collection_id = collection->collection_id;
+    model->resource_id = collection->resource_id;
 }
 
 
 }
 
 
@@ -111,7 +111,7 @@ static void notebook_loaded(UiEvent *event, AsyncListResult *result, void *data)
 }
 
 void notebookmodel_reload(UiObject *obj, NotebookModel *model) {
 }
 
 void notebookmodel_reload(UiObject *obj, NotebookModel *model) {
-    note_store_get_notes_async(obj, model->collection_id, notebook_loaded, model);
+    note_store_get_notes_async(obj, model->resource_id, notebook_loaded, model);
 }
 
 
 }
 
 
@@ -200,7 +200,7 @@ void notebookmodel_new_note(NotebookModel *model) {
     Resource *new_note = cxMalloc(model->current_notes_pool->allocator, sizeof(Resource));
     memset(new_note, 0, sizeof(Resource));
     
     Resource *new_note = cxMalloc(model->current_notes_pool->allocator, sizeof(Resource));
     memset(new_note, 0, sizeof(Resource));
     
-    new_note->parent_id = model->collection->collection_id;
+    new_note->parent_id = model->collection->resource_id;
     notebookmodel_attach_note(model, new_note);
     new_note->model->modified = TRUE;
     // initialize note content
     notebookmodel_attach_note(model, new_note);
     new_note->model->modified = TRUE;
     // initialize note content
@@ -251,8 +251,8 @@ void notebookmodel_add2navstack(NotebookModel *model) {
     
     // init NavStack element
     NavStack nav;
     
     // init NavStack element
     NavStack nav;
-    nav.collection_id = model->collection_id;
-    nav.note_id = model->current_note ? model->current_note->resource_id : 0;
+    nav.collection_resource_id = model->resource_id;
+    nav.resource_id = model->current_note ? model->current_note->resource_id : 0;
     nav.view_flags = 0;
     
     if(!list_visible) {
     nav.view_flags = 0;
     
     if(!list_visible) {
index 205bd2f09ef1636f2f070da649a00ffce85e411a..e031863439beb2f7108c39567d785827b11638e0 100644 (file)
@@ -42,7 +42,7 @@ void notebookmodel_attach(MainWindow *window, NotebookModel *model);
 
 void notebookmodel_detach(NotebookModel *model);
 
 
 void notebookmodel_detach(NotebookModel *model);
 
-void notebookmodel_set_collection(NotebookModel *model, Collection *collection);
+void notebookmodel_set_collection(NotebookModel *model, Resource *collection);
 
 void notebookmodel_reload(UiObject *obj, NotebookModel *model);
 
 
 void notebookmodel_reload(UiObject *obj, NotebookModel *model);
 
index 0c55f2afaffb1a51697f4435bb7849856c3e54db..ad88c97eb6cdcbfce13538455560c8f3d10436e5 100644 (file)
 #include "cx/mempool.h"
 
 #define SQL_NOTEBOOK_STRUCTURE "with recursive cols as (\n" \
 #include "cx/mempool.h"
 
 #define SQL_NOTEBOOK_STRUCTURE "with recursive cols as (\n" \
-    "    select 1 as depth, '' as path, * from collections where collection_id = ?\n" \
+    "    select 1 as depth, '' as path, * from resources where resource_id = ?\n" \
     "    union all\n" \
     "    union all\n" \
-    "    select p.depth+1 as depth, concat(p.path, '/', c.name), c.* from collections c\n" \
-    "    inner join cols p on c.parent_id = p.collection_id\n" \
+    "    select p.depth+1 as depth, concat(p.path, '/', c.nodename), c.* from resources c\n" \
+    "    inner join cols p on c.parent_id = p.resource_id and c.iscollection = 1 \n" \
     "    where p.depth < 3\n" \
     ")\n" \
     "select * from cols\n" \
     "order by path;"
 
     "    where p.depth < 3\n" \
     ")\n" \
     "select * from cols\n" \
     "order by path;"
 
-#define SQL_NOTEBOOK_GET_NOTES "select resource_id, parent_id, name, title, lastmodified, creationdate, contenttype, created_by from resources where parent_id = ? and bin_content is NULL;"
+#define SQL_NOTEBOOK_GET_NOTES "select resource_id, parent_id, nodename, displayname, lastmodified, creationdate, iscollection, contenttype from resources where parent_id = ? ;"
 
 
-#define SQL_NOTE_GET_CONTENT "select content, bin_content from resources where resource_id = ? ;"
+#define SQL_NOTE_GET_CONTENT "select content from resources where resource_id = ? ;"
 
 
-#define SQL_NOTE_NEW "insert into resources(parent_id, name, title, lastmodified, creationdate, content) values (?, ?, ?, datetime(), datetime(), ?) returning resource_id;"
+#define SQL_NOTE_NEW "insert into resources(parent_id, nodename, displayname, lastmodified, creationdate, content) values (?, ?, ?, unixepoch(), unixepoch(), ?) returning resource_id;"
 
 
-#define SQL_NOTE_SAVE "update resources set name = ? ," \
-    "title = ? ," \
-    "content = ? " \
+#define SQL_NOTE_SAVE "update resources set nodename = ? ," \
+    "displayname = ? ," \
+    "content = ?, " \
+    "lastmodified = unixepoch() " \
     "where resource_id = ? ;"
 
 #define SQL_NOTE_MOVE_TO_TRASH "update resources set parent_id = ? where resource_id = ? ;"
 
 #define SQL_NOTE_DELETE "delete from resources where resource_id = ? ;"
 
     "where resource_id = ? ;"
 
 #define SQL_NOTE_MOVE_TO_TRASH "update resources set parent_id = ? where resource_id = ? ;"
 
 #define SQL_NOTE_DELETE "delete from resources where resource_id = ? ;"
 
-#define SQL_ATTACHMENT_RESOURCE_NEW "insert into resources(parent_id, name, lastmodified, creationdate, bin_content) values ((select parent_id from resources where resource_id = ?), ?, datetime(), datetime(), ?) returning resource_id;"
+#define SQL_ATTACHMENT_RESOURCE_NEW "insert into resources(parent_id, nodename, lastmodified, creationdate, content) values ((select parent_id from resources where resource_id = ?), ?, unixepoch(), unixepoch(), ?) returning resource_id;"
 #define SQL_ATTACHMENT_NEW "insert into attachments(attachment_resource_id, parent_resource_id, type) values (?, ?, ?) returning attachment_id;"
 
 #define SQL_ATTACHMENTS_GET "select attachment_id, attachment_resource_id, parent_resource_id, a.type, "\
 #define SQL_ATTACHMENT_NEW "insert into attachments(attachment_resource_id, parent_resource_id, type) values (?, ?, ?) returning attachment_id;"
 
 #define SQL_ATTACHMENTS_GET "select attachment_id, attachment_resource_id, parent_resource_id, a.type, "\
-    "r.name, r.bin_content from attachments a "\
+    "r.name, r.content from attachments a "\
     "inner join resources r on a.attachment_resource_id = r.resource_id " \
     "where parent_resource_id = ? order by attachment_id;"
 
     "inner join resources r on a.attachment_resource_id = r.resource_id " \
     "where parent_resource_id = ? order by attachment_id;"
 
@@ -138,19 +139,19 @@ CxList* note_store_get_user_settings(const CxAllocator *a, const char *host, con
     DBUQuery *query = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(query, "select * from user_settings;");
     if(host) {
     DBUQuery *query = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(query, "select * from user_settings;");
     if(host) {
-        dbuQuerySetParamString(query, 0, cx_str(host));
+        dbuQuerySetParamString(query, 1, cx_str(host));
     } else {
     } else {
-        dbuQuerySetParamNull(query, 0);
+        dbuQuerySetParamNull(query, 1);
     }
     if(user) {
     }
     if(user) {
-        dbuQuerySetParamString(query, 1, cx_str(user));
+        dbuQuerySetParamString(query, 2, cx_str(user));
     } else {
     } else {
-        dbuQuerySetParamNull(query, 1);
+        dbuQuerySetParamNull(query, 2);
     }
     if(profile) {
     }
     if(profile) {
-        dbuQuerySetParamString(query, 2, cx_str(profile));
+        dbuQuerySetParamString(query, 3, cx_str(profile));
     } else {
     } else {
-        dbuQuerySetParamNull(query, 2);
+        dbuQuerySetParamNull(query, 3);
     }
     
     DBUObjectBuilder *builder = dbuObjectBuilder(usersettings_class, query, a);
     }
     
     DBUObjectBuilder *builder = dbuObjectBuilder(usersettings_class, query, a);
@@ -220,7 +221,7 @@ int note_store_create_default(const char *host, const char *user) {
     const char *profile_name = "default";
     
     const char *sql1 = "insert into repositories(name, local_path) values ('default', '$documents/notes') returning repository_id;";
     const char *profile_name = "default";
     
     const char *sql1 = "insert into repositories(name, local_path) values ('default', '$documents/notes') returning repository_id;";
-    const char *sql2 = "insert into collections(repository_id, name) values (?, ?) returning collection_id;";
+    const char *sql2 = "insert into resources(nodename, iscollection) values (?, 1) returning resource_id;";
     const char *sql3 = "insert into user_settings(host, user, profile_name, default_repository_id, default_collection_id) values (?, ?, ?, ?, ?)";
     
     int err = 1;
     const char *sql3 = "insert into user_settings(host, user, profile_name, default_repository_id, default_collection_id) values (?, ?, ?, ?, ?)";
     
     int err = 1;
@@ -280,18 +281,16 @@ int note_store_create_default(const char *host, const char *user) {
     
     err = 1;
     DBUQuery *q = connection->createQuery(connection, NULL);
     
     err = 1;
     DBUQuery *q = connection->createQuery(connection, NULL);
-    dbuQuerySetSQL(q, "insert into collections(parent_id, repository_id, name) values (?, ?, 'Notebooks') returning collection_id;");
+    dbuQuerySetSQL(q, "insert into resources(parent_id, nodename, iscollection) values (?, 'Notebooks', 1) returning resource_id;");
     dbuQuerySetParamInt64(q, 1, collection_id);
     dbuQuerySetParamInt64(q, 1, collection_id);
-    dbuQuerySetParamInt64(q, 2, repo_id);
     if(!q->exec(q)) {
         err = dbuResultAsValue(q->getResult(q), &notebooks_id);
     }
     dbuQueryFree(q);
     
     q = connection->createQuery(connection, NULL);
     if(!q->exec(q)) {
         err = dbuResultAsValue(q->getResult(q), &notebooks_id);
     }
     dbuQueryFree(q);
     
     q = connection->createQuery(connection, NULL);
-    dbuQuerySetSQL(q, "insert into collections(parent_id, repository_id, name) values (?, ?, 'My Notes') returning collection_id;");
+    dbuQuerySetSQL(q, "insert into resources(parent_id, nodename, iscollection) values (?, 'My Notes', 1) returning resource_id;");
     dbuQuerySetParamInt64(q, 1, notebooks_id);
     dbuQuerySetParamInt64(q, 1, notebooks_id);
-    dbuQuerySetParamInt64(q, 2, repo_id);
     if(!q->exec(q)) {
         err = dbuResultAsValue(q->getResult(q), &mynotes_id);
     }
     if(!q->exec(q)) {
         err = dbuResultAsValue(q->getResult(q), &mynotes_id);
     }
@@ -312,7 +311,7 @@ void note_store_reload() {
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTEBOOK_STRUCTURE);
     dbuQuerySetParamInt64(q, 1, settings_default_node_id);
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTEBOOK_STRUCTURE);
     dbuQuerySetParamInt64(q, 1, settings_default_node_id);
-    DBUObjectBuilder *builder = dbuObjectBuilder(collection_class, q, mp->allocator);
+    DBUObjectBuilder *builder = dbuObjectBuilder(resource_class, q, mp->allocator);
     CxList *collections = dbuObjectBuilderGetList(builder);
     dbuObjectBuilderDestroy(builder);
     
     CxList *collections = dbuObjectBuilderGetList(builder);
     dbuObjectBuilderDestroy(builder);
     
@@ -333,8 +332,8 @@ void note_store_reload() {
     
     // convert result list to tree
     CxIterator i = cxListIterator(collections);
     
     // convert result list to tree
     CxIterator i = cxListIterator(collections);
-    cx_foreach(Collection *, col, i) {
-        CxHashKey key = cx_hash_key(&col->collection_id, sizeof(int64_t));
+    cx_foreach(Resource *, col, i) {
+        CxHashKey key = cx_hash_key(&col->resource_id, sizeof(int64_t));
         cxMapPut(collection_map, key, col);
         if(i.index == 0) {
             store->root = col;
         cxMapPut(collection_map, key, col);
         if(i.index == 0) {
             store->root = col;
@@ -342,7 +341,7 @@ void note_store_reload() {
         }
         if(col->parent_id != 0) {
             CxHashKey parent_key = cx_hash_key(&col->parent_id, sizeof(int64_t));
         }
         if(col->parent_id != 0) {
             CxHashKey parent_key = cx_hash_key(&col->parent_id, sizeof(int64_t));
-            Collection *parent = cxMapGet(collection_map, parent_key);
+            Resource *parent = cxMapGet(collection_map, parent_key);
             if(parent) {
                 if(!parent->children) {
                     parent->children = cxArrayListCreate(a, NULL, CX_STORE_POINTERS, 8);
             if(parent) {
                 if(!parent->children) {
                     parent->children = cxArrayListCreate(a, NULL, CX_STORE_POINTERS, 8);
@@ -370,14 +369,14 @@ CxList* note_store_get_notes(const CxAllocator *a, int64_t parent_collection_id)
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTEBOOK_GET_NOTES);
     dbuQuerySetParamInt64(q, 1, parent_collection_id);
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTEBOOK_GET_NOTES);
     dbuQuerySetParamInt64(q, 1, parent_collection_id);
-    DBUObjectBuilder *builder = dbuObjectBuilder(notes_class, q, a);
+    DBUObjectBuilder *builder = dbuObjectBuilder(resource_class, q, a);
     CxList *notes = dbuObjectBuilderGetList(builder);
     dbuObjectBuilderDestroy(builder);
     return notes;
 }
 
 typedef struct JobGetNotes {
     CxList *notes = dbuObjectBuilderGetList(builder);
     dbuObjectBuilderDestroy(builder);
     return notes;
 }
 
 typedef struct JobGetNotes {
-    int64_t collection_id;
+    int64_t parent_resource_id;
     listresult_func resultcb;
     void *userdata;
     AsyncListResult result;
     listresult_func resultcb;
     void *userdata;
     AsyncListResult result;
@@ -393,7 +392,7 @@ static void uithr_get_notes_finished(UiEvent *event, JobGetNotes *job) {
 }
 
 static int qthr_get_notes(JobGetNotes *job) {
 }
 
 static int qthr_get_notes(JobGetNotes *job) {
-    job->result.list = note_store_get_notes(job->result.mp->allocator, job->collection_id);
+    job->result.list = note_store_get_notes(job->result.mp->allocator, job->parent_resource_id);
     if(!job->result.list) {
         cxMempoolFree(job->result.mp);
         job->result.mp = NULL;
     if(!job->result.list) {
         cxMempoolFree(job->result.mp);
         job->result.mp = NULL;
@@ -401,11 +400,11 @@ static int qthr_get_notes(JobGetNotes *job) {
     return 0;
 }
 
     return 0;
 }
 
-void note_store_get_notes_async(UiObject* obj, int64_t parent_collection_id, listresult_func resultcb, void *userdata) {
+void note_store_get_notes_async(UiObject* obj, int64_t parent_resource_id, listresult_func resultcb, void *userdata) {
     JobGetNotes *job = malloc(sizeof(JobGetNotes));
     job->result.mp = cxMempoolCreateSimple(128);
     job->result.list = NULL;
     JobGetNotes *job = malloc(sizeof(JobGetNotes));
     job->result.mp = cxMempoolCreateSimple(128);
     job->result.list = NULL;
-    job->collection_id = parent_collection_id;
+    job->parent_resource_id = parent_resource_id;
     job->resultcb = resultcb;
     job->userdata = userdata;
     ui_threadpool_job(queue, obj, (ui_threadfunc)qthr_get_notes, job, (ui_callback)uithr_get_notes_finished, job);
     job->resultcb = resultcb;
     job->userdata = userdata;
     ui_threadpool_job(queue, obj, (ui_threadfunc)qthr_get_notes, job, (ui_callback)uithr_get_notes_finished, job);
@@ -504,8 +503,8 @@ static int qthr_new_note(SaveNoteJob *job) {
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTE_NEW);
     dbuQuerySetParamInt64(q, 1, n->parent_id);
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTE_NEW);
     dbuQuerySetParamInt64(q, 1, n->parent_id);
-    dbuQuerySetParamString(q, 2, cx_str(n->title));
-    dbuQuerySetParamString(q, 3, cx_str(n->title));
+    dbuQuerySetParamString(q, 2, cx_str(n->displayname));
+    dbuQuerySetParamString(q, 3, cx_str(n->displayname));
     dbuQuerySetParamString(q, 4, cx_strcast(n->content));
     if(dbuQueryExec(q)) {
         job->error = 1;
     dbuQuerySetParamString(q, 4, cx_strcast(n->content));
     if(dbuQueryExec(q)) {
         job->error = 1;
@@ -542,8 +541,8 @@ static int qthr_save_note(SaveNoteJob *job) {
     Resource *n = job->note;
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTE_SAVE);
     Resource *n = job->note;
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTE_SAVE);
-    dbuQuerySetParamString(q, 1, cx_str(n->title));
-    dbuQuerySetParamString(q, 2, cx_str(n->title));
+    dbuQuerySetParamString(q, 1, cx_str(n->displayname));
+    dbuQuerySetParamString(q, 2, cx_str(n->displayname));
     dbuQuerySetParamString(q, 3, cx_strcast(n->content));
     dbuQuerySetParamInt64(q, 4, n->resource_id);
     if(dbuQueryExec(q)) {
     dbuQuerySetParamString(q, 3, cx_strcast(n->content));
     dbuQuerySetParamInt64(q, 4, n->resource_id);
     if(dbuQueryExec(q)) {
@@ -582,7 +581,7 @@ static int qthr_delete_note(DeleteNoteJob *job) {
     DBUQuery *q = connection->createQuery(connection, NULL);
     if(job->move_to_trash && current_store->trash) {
         dbuQuerySetSQL(q, SQL_NOTE_MOVE_TO_TRASH);
     DBUQuery *q = connection->createQuery(connection, NULL);
     if(job->move_to_trash && current_store->trash) {
         dbuQuerySetSQL(q, SQL_NOTE_MOVE_TO_TRASH);
-        dbuQuerySetParamInt64(q, 1, current_store->trash->collection_id);
+        dbuQuerySetParamInt64(q, 1, current_store->trash->resource_id);
         dbuQuerySetParamInt64(q, 2, job->note_id);
     } else {
         dbuQuerySetSQL(q, SQL_NOTE_DELETE);
         dbuQuerySetParamInt64(q, 2, job->note_id);
     } else {
         dbuQuerySetSQL(q, SQL_NOTE_DELETE);
@@ -684,7 +683,7 @@ void note_store_save_attachment_async(UiObject *obj, Attachment *attachment, exe
     job->attachment_id = attachment->attachment_id;
     job->type = attachment->type;
     job->name = strdup(attachment->name);
     job->attachment_id = attachment->attachment_id;
     job->type = attachment->type;
     job->name = strdup(attachment->name);
-    job->content = attachment->bin_content.ptr ? cx_strdup(cx_strcast(attachment->bin_content)) : (cxmutstr){NULL, 0};
+    job->content = attachment->content.ptr ? cx_strdup(cx_strcast(attachment->content)) : (cxmutstr){NULL, 0};
     job->resultcb = resultcb;
     job->error = 0;
     job->userdata = userdata;
     job->resultcb = resultcb;
     job->error = 0;
     job->userdata = userdata;
index 131bcc3495dc5112315648211a6a7abc30d9993e..84405b341af115b6e09500962bdf23eebea9a599 100644 (file)
@@ -42,8 +42,8 @@ extern "C" {
       
 typedef struct NoteStore {
     CxMempool *mp;
       
 typedef struct NoteStore {
     CxMempool *mp;
-    Collection *root;
-    Collection *trash;
+    Resource *root;
+    Resource *trash;
 } NoteStore;
     
 typedef struct AsyncListResult {
 } NoteStore;
     
 typedef struct AsyncListResult {
@@ -82,7 +82,7 @@ void note_store_reload();
 NoteStore* note_store_get();
 
 CxList* note_store_get_notes(const CxAllocator *a, int64_t parent_collection_id);
 NoteStore* note_store_get();
 
 CxList* note_store_get_notes(const CxAllocator *a, int64_t parent_collection_id);
-void note_store_get_notes_async(UiObject *obj, int64_t parent_collection_id, listresult_func resultcb, void *userdata);
+void note_store_get_notes_async(UiObject *obj, int64_t parent_resource_id, listresult_func resultcb, void *userdata);
 
 cxmutstr note_store_get_note_content(const CxAllocator *a, int64_t note_id);
 void note_store_get_note_content_async(UiObject *obj, const CxAllocator *a, int64_t note_id, stringresult_func resultcb, void *userdata);
 
 cxmutstr note_store_get_note_content(const CxAllocator *a, int64_t note_id);
 void note_store_get_note_content_async(UiObject *obj, const CxAllocator *a, int64_t note_id, stringresult_func resultcb, void *userdata);
index aca8c822254f497ca8823ba29570736c981e6be4..396fa39287e51d73f1bbd8cf1d870dd6d29d1542 100644 (file)
     "default_key   text, " \
     "authmethod    integer, " \
     "local_path    text);" 
     "default_key   text, " \
     "authmethod    integer, " \
     "local_path    text);" 
-#define SQL_CREATE_TABLE_COLLECTIONS "create table collections( " \
-    "collection_id           integer primary key, " \
-    "parent_id               integer, " \
-    "repository_id           integer, " \
-    "name                    text not null, " \
-    "display_name            text, " \
-    "type                    text, " \
-    "created_by              text, " \
-    "created_at              text, " \
-    "foreign key (parent_id) references collections(collection_id), " \
-    "foreign key (repository_id) references repositories(repository_id)" \
-    ");"
 #define SQL_CREATE_TABLE_USER_SETTINGS "create table user_settings( " \
     "host                    text, " \
     "user                    text, " \
 #define SQL_CREATE_TABLE_USER_SETTINGS "create table user_settings( " \
     "host                    text, " \
     "user                    text, " \
     "created_by              text, " \
     "created_at              text, " \
     "foreign key (default_repository_id) references repositories(repository_id), " \
     "created_by              text, " \
     "created_at              text, " \
     "foreign key (default_repository_id) references repositories(repository_id), " \
-    "foreign key (default_collection_id) references collections(collection_id), " \
+    "foreign key (default_collection_id) references resources(resource_id), " \
     "unique (host, user, profile_name)" \
     ");"
     "unique (host, user, profile_name)" \
     ");"
-#define SQL_CREATE_TABLE_NOTES "create table resources( " \
+#define SQL_CREATE_TABLE_RESOURCES "create table resources( " \
     "resource_id             integer primary key, " \
     "parent_id               integer, " \
     "resource_id             integer primary key, " \
     "parent_id               integer, " \
-    "name                    text, " \
-    "title                   text, " \
-    "lastmodified            text, " \
-    "creationdate            text, " \
+    "nodename                text, " \
+    "displayname             text, " \
+    "iscollection            integer, " \
     "contenttype             text, " \
     "contenttype             text, " \
-    "content                 text, " \
-    "bin_content             blob, " \
-    "created_by              text, " \
-    "foreign key (parent_id) references collections(collection_id), " \
-    "unique (parent_id, name) " \
+    "contentlength           integer, " \
+    "lastmodified            integer, " \
+    "creationdate            integer, " \
+    "etag                    text, " \
+    "content                 blob, " \
+    "foreign key (parent_id) references resources(resource_id), " \
+    "unique (parent_id, nodename) " \
     ");"
 #define SQL_CREATE_TABLE_ATTACHMENTS "create table attachments( " \
     "attachment_id           integer primary key, " \
     ");"
 #define SQL_CREATE_TABLE_ATTACHMENTS "create table attachments( " \
     "attachment_id           integer primary key, " \
@@ -94,9 +83,8 @@ int store_sqlite_init_db(DBUConnection *connection) {
     char *sql[] = { 
         SQL_CREATE_TABLE_NOTEDB,
         SQL_CREATE_TABLE_REPOSITORIES,
     char *sql[] = { 
         SQL_CREATE_TABLE_NOTEDB,
         SQL_CREATE_TABLE_REPOSITORIES,
-        SQL_CREATE_TABLE_COLLECTIONS,
         SQL_CREATE_TABLE_USER_SETTINGS,
         SQL_CREATE_TABLE_USER_SETTINGS,
-        SQL_CREATE_TABLE_NOTES,
+        SQL_CREATE_TABLE_RESOURCES,
         SQL_CREATE_TABLE_ATTACHMENTS
     };
     int nsql = sizeof(sql) / sizeof(char*);
         SQL_CREATE_TABLE_ATTACHMENTS
     };
     int nsql = sizeof(sql) / sizeof(char*);
index d87c856e3db56b766fef3d80a4ae94ef53ace8f6..178f2d3a1a93794be91da92d5aa33bf95c30d7dc 100644 (file)
@@ -32,8 +32,7 @@ static DBUContext *ctx;
 
 DBUClass *usersettings_class;
 DBUClass *repository_class;
 
 DBUClass *usersettings_class;
 DBUClass *repository_class;
-DBUClass *collection_class;
-DBUClass *notes_class;
+DBUClass *resource_class;
 DBUClass *attachments_class;
 
 DBUContext* get_dbu_context() {
 DBUClass *attachments_class;
 
 DBUContext* get_dbu_context() {
@@ -51,25 +50,17 @@ void register_types() {
     dbuClassAdd(usersettings_class, UserSettings, default_collection_id);
     dbuClassAdd(usersettings_class, UserSettings, created_by);
     
     dbuClassAdd(usersettings_class, UserSettings, default_collection_id);
     dbuClassAdd(usersettings_class, UserSettings, created_by);
     
-    collection_class = dbuRegisterClass(ctx, "collections", Collection, collection_id);
-    dbuClassAdd(collection_class, Collection, parent_id);
-    dbuClassAdd(collection_class, Collection, repository_id);
-    dbuClassAdd(collection_class, Collection, name);
-    dbuClassAdd(collection_class, Collection, display_name);
-    dbuClassAdd(collection_class, Collection, type);
-    
-    notes_class = dbuRegisterClass(ctx, "resources", Resource, resource_id);
-    dbuClassAdd(notes_class, Resource, parent_id);
-    dbuClassAdd(notes_class, Resource, name);
-    dbuClassAdd(notes_class, Resource, title);
-    dbuClassAdd(notes_class, Resource, lastmodified);
-    dbuClassAdd(notes_class, Resource, creationdate);
-    dbuClassAdd(notes_class, Resource, contenttype);
-    dbuClassAdd(notes_class, Resource, contentlength);
-    dbuClassAdd(notes_class, Resource, content);
-    dbuClassAdd(notes_class, Resource, bin_content);
-    dbuClassAdd(notes_class, Resource, created_by);
-    dbuClassAdd(notes_class, Resource, content_loaded);
+    resource_class = dbuRegisterClass(ctx, "resources", Resource, resource_id);
+    dbuClassAdd(resource_class, Resource, parent_id);
+    dbuClassAdd(resource_class, Resource, nodename);
+    dbuClassAdd(resource_class, Resource, displayname);
+    dbuClassAdd(resource_class, Resource, lastmodified);
+    dbuClassAdd(resource_class, Resource, creationdate);
+    dbuClassAdd(resource_class, Resource, contenttype);
+    dbuClassAdd(resource_class, Resource, contentlength);
+    dbuClassAdd(resource_class, Resource, content);
+    //dbuClassAdd(notes_class, Resource, created_by);
+    dbuClassAdd(resource_class, Resource, content_loaded);
     
     repository_class = dbuRegisterClass(ctx, "repositories", Repository, repository_id);
     dbuClassAdd(repository_class, Repository, name);
     
     repository_class = dbuRegisterClass(ctx, "repositories", Repository, repository_id);
     dbuClassAdd(repository_class, Repository, name);
@@ -84,5 +75,5 @@ void register_types() {
     dbuClassAdd(attachments_class, Attachment, parent_resource_id);
     dbuClassAdd(attachments_class, Attachment, type);
     dbuClassAdd(attachments_class, Attachment, name);
     dbuClassAdd(attachments_class, Attachment, parent_resource_id);
     dbuClassAdd(attachments_class, Attachment, type);
     dbuClassAdd(attachments_class, Attachment, name);
-    dbuClassAdd(attachments_class, Attachment, bin_content);
+    dbuClassAdd(attachments_class, Attachment, content);
 }
 }
index d107db8a88396de5be300ee6e22f2c8e638e07e8..2dd8bc3b0031d8b9465efe2c78e9adbc823f9847 100644 (file)
@@ -44,8 +44,9 @@ typedef struct AttachmentModel AttachmentModel;
 
 typedef struct UserSettings UserSettings;
 typedef struct Repository   Repository;
 
 typedef struct UserSettings UserSettings;
 typedef struct Repository   Repository;
-typedef struct Collection   Collection;
 typedef struct Resource     Resource;
 typedef struct Resource     Resource;
+typedef struct Notebook     Notebook;
+typedef struct Note         Note;
 typedef struct Attachment   Attachment;
     
 struct UserSettings {
 typedef struct Attachment   Attachment;
     
 struct UserSettings {
@@ -68,29 +69,24 @@ struct Repository {
     bool       encryption;
 };
 
     bool       encryption;
 };
 
-struct Collection {
-    int64_t    collection_id;
-    int64_t    parent_id;
-    int64_t    repository_id;
-    char       *name;
-    char       *display_name;
-    char       *type;
-    
-    CxList     *children;
-};
-
 struct Resource {
     int64_t    resource_id;
     int64_t    parent_id;
 struct Resource {
     int64_t    resource_id;
     int64_t    parent_id;
-    char       *name;
-    char       *title;
-    char       *lastmodified;
-    char       *creationdate;
+    char       *nodename;
+    char       *displayname;
     char       *contenttype;
     uint64_t   contentlength;
     char       *contenttype;
     uint64_t   contentlength;
-    cxmutstr   content;
-    cxmutstr   bin_content;
-    char       *created_by;
+    cxmutstr   etag;
+    cxmutstr   content; 
+    bool       iscollection;
+    time_t     lastmodified;
+    time_t     creationdate;
+    
+    
+    /*
+     * type: Resource*
+     */
+    CxList     *children;
     
     /*
      * type: Attachment*
     
     /*
      * type: Attachment*
@@ -110,6 +106,13 @@ struct Resource {
     NoteModel  *model;
 };
 
     NoteModel  *model;
 };
 
+struct Notebook {
+    int64_t    notebook_id;
+    int64_t    resource_id;
+    int64_t    repository_id;
+    int        type;
+};
+
 typedef enum AttachmentType {
     NOTE_ATTACHMENT_FILE = 0,
     NOTE_ATTACHMENT_IMAGE
 typedef enum AttachmentType {
     NOTE_ATTACHMENT_FILE = 0,
     NOTE_ATTACHMENT_IMAGE
@@ -124,7 +127,7 @@ struct Attachment {
     
     // temp (from table resources)
     char       *name;
     
     // temp (from table resources)
     char       *name;
-    cxmutstr   bin_content;
+    cxmutstr   content;
     bool       content_loaded;
     
     // is the attachment content already stored persistently
     bool       content_loaded;
     
     // is the attachment content already stored persistently
@@ -143,7 +146,7 @@ struct Attachment {
 extern DBUClass *usersettings_class;
 extern DBUClass *repository_class;
 extern DBUClass *collection_class;
 extern DBUClass *usersettings_class;
 extern DBUClass *repository_class;
 extern DBUClass *collection_class;
-extern DBUClass *notes_class;
+extern DBUClass *resource_class;
 extern DBUClass *attachments_class;
 
 void register_types();
 extern DBUClass *attachments_class;
 
 void register_types();
index 8cfe55df7d51f2310d1dbb936bebef0f0ce95f21..80fd2ad918cf44945ab4c3b98bc70b93c95ffd3d 100644 (file)
@@ -134,8 +134,8 @@ void window_notelist_setvisible(MainWindow *window, UiBool visible) {
 }
 
 void window_sidebar_getvalue(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item) {
 }
 
 void window_sidebar_getvalue(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item) {
-    Collection *notebook = rowdata;
-    item->label = strdup(notebook->display_name ? notebook->display_name : notebook->name);
+    Resource *notebook = rowdata;
+    item->label = strdup(notebook->displayname ? notebook->displayname : notebook->nodename);
     
 }
 
     
 }
 
@@ -175,16 +175,16 @@ void update_sublists(UiContext *ctx, UiList *sublists) {
     }
     
     i = cxListIterator(notestore->root->children);
     }
     
     i = cxListIterator(notestore->root->children);
-    cx_foreach(Collection *, col, i) {
+    cx_foreach(Resource *, col, i) {
         UiSubList *sublist = calloc(1, sizeof(UiSubList));
         UiSubList *sublist = calloc(1, sizeof(UiSubList));
-        sublist->header = strdup(col->display_name ? col->display_name : col->name);
+        sublist->header = strdup(col->displayname ? col->displayname : col->nodename);
         sublist->value = ui_list_new(ctx, NULL);
         sublist->userdata = col;
         ui_list_append(sublists, sublist);
         
         if(col->children) {
             CxIterator j = cxListIterator(col->children);
         sublist->value = ui_list_new(ctx, NULL);
         sublist->userdata = col;
         ui_list_append(sublists, sublist);
         
         if(col->children) {
             CxIterator j = cxListIterator(col->children);
-            cx_foreach(Collection *, nb, j) {
+            cx_foreach(Resource *, nb, j) {
                 ui_list_append(sublist->value, nb);
             }
         }
                 ui_list_append(sublist->value, nb);
             }
         }
@@ -204,10 +204,11 @@ void* window_notelist_getvalue(void *data, int col) {
     Resource *note = data;
     switch(col) {
         case 0: {
     Resource *note = data;
     switch(col) {
         case 0: {
-            return note->title ? note->title : note->name;
+            return note->displayname ? note->displayname : note->nodename;
         }
         case 1: {
         }
         case 1: {
-            return note->lastmodified ? strdup(note->lastmodified) : NULL;
+            //return note->lastmodified ? strdup(note->lastmodified) : NULL;
+            return NULL; // TODO
         }
     }
     
         }
     }
     
@@ -225,18 +226,18 @@ NotebookModel* window_get_cached_notebook(MainWindow *window, int64_t collection
  * Loads the notebook/note with UI options specified by the NavStack element
  */
 void window_navigate(MainWindow *window, NavStack *nav) {
  * Loads the notebook/note with UI options specified by the NavStack element
  */
 void window_navigate(MainWindow *window, NavStack *nav) {
-    if(nav->collection_id == 0) {
+    if(nav->collection_resource_id == 0) {
         return;
     }
     // when a collection is on the navstack, it should also be in the cache
         return;
     }
     // when a collection is on the navstack, it should also be in the cache
-    NotebookModel *notebook = window_get_cached_notebook(window, nav->collection_id);
+    NotebookModel *notebook = window_get_cached_notebook(window, nav->collection_resource_id);
     if(!notebook) {
         printf("window_navigate: notebook not in cache\n");
         return;
     }
     
     size_t note_index;
     if(!notebook) {
         printf("window_navigate: notebook not in cache\n");
         return;
     }
     
     size_t note_index;
-    Resource *note = nav->note_id != 0 ? notebookmodel_get_note_by_id(notebook, nav->note_id, &note_index) : NULL;
+    Resource *note = nav->resource_id != 0 ? notebookmodel_get_note_by_id(notebook, nav->resource_id, &note_index) : NULL;
     
     window_notelist_setvisible(window, !(nav->view_flags & VIEW_FLAGS_NO_BROWSER));
     
     
     window_notelist_setvisible(window, !(nav->view_flags & VIEW_FLAGS_NO_BROWSER));
     
@@ -256,9 +257,9 @@ void window_navigate(MainWindow *window, NavStack *nav) {
 void action_notebook_selected(UiEvent *event, void *userdata) {
     UiSubListEventData *data = event->eventdata;
     MainWindow *window = event->window;
 void action_notebook_selected(UiEvent *event, void *userdata) {
     UiSubListEventData *data = event->eventdata;
     MainWindow *window = event->window;
-    Collection *collection = data->row_data;
+    Resource *collection = data->row_data;
     
     
-    printf("notebook selected: %s\n", collection->name);
+    printf("notebook selected: %s\n", collection->nodename);
     ui_set_group(event->obj->ctx, APP_STATE_NOTEBOOK_SELECTED);
     
     if(window->current_notebook && window->current_notebook->collection == collection) {
     ui_set_group(event->obj->ctx, APP_STATE_NOTEBOOK_SELECTED);
     
     if(window->current_notebook && window->current_notebook->collection == collection) {
@@ -268,7 +269,7 @@ void action_notebook_selected(UiEvent *event, void *userdata) {
     // reset splitpane visibility
     window_notelist_setvisible(window, TRUE);
     
     // reset splitpane visibility
     window_notelist_setvisible(window, TRUE);
     
-    CxHashKey key = cx_hash_key(&collection->collection_id, sizeof(collection->collection_id));
+    CxHashKey key = cx_hash_key(&collection->resource_id, sizeof(collection->resource_id));
     NotebookModel *notebook = cxMapGet(window->notebook_cache, key);
     if(!notebook) {
         printf("notebook not in cache\n");
     NotebookModel *notebook = cxMapGet(window->notebook_cache, key);
     if(!notebook) {
         printf("notebook not in cache\n");
index 981b161069a1783d0284bc688b1802bf56338a75..7e91f7374c12a9562f0a849752b9642d5d61c6e6 100644 (file)
 #include <string.h>
 #include <errno.h>
 
 #include <string.h>
 #include <errno.h>
 
+#ifdef _WIN32
+#include <Windows.h>
+#include <sysinfoapi.h>
+static unsigned long system_page_size() {
+    static unsigned long ps = 0;
+    if (ps == 0) {
+        SYSTEM_INFO sysinfo;
+        GetSystemInfo(&sysinfo);
+        ps = sysinfo.dwPageSize;
+    }
+    return ps;
+}
+#define SYSTEM_PAGE_SIZE system_page_size()
+#else
+#include <unistd.h>
+#define SYSTEM_PAGE_SIZE sysconf(_SC_PAGESIZE)
+#endif
+
 static int buffer_copy_on_write(CxBuffer* buffer) {
     if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
     void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
 static int buffer_copy_on_write(CxBuffer* buffer) {
     if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
     void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
@@ -147,11 +165,16 @@ int cxBufferSeek(
     npos += offset;
 
     if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
     npos += offset;
 
     if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
-        errno = EOVERFLOW;
+        // to be compliant with fseek() specification
+        // we return EINVAL on underflow
+        errno = EINVAL;
         return -1;
     }
 
     if (npos > buffer->size) {
         return -1;
     }
 
     if (npos > buffer->size) {
+        // not compliant with fseek() specification
+        // but this is the better behavior for CxBuffer
+        errno = EINVAL;
         return -1;
     } else {
         buffer->pos = npos;
         return -1;
     } else {
         buffer->pos = npos;
@@ -185,6 +208,28 @@ int cxBufferMinimumCapacity(
         return 0;
     }
 
         return 0;
     }
 
+    unsigned long pagesize = SYSTEM_PAGE_SIZE;
+    // if page size is larger than 64 KB - for some reason - truncate to 64 KB
+    if (pagesize > 65536) pagesize = 65536;
+    if (newcap < pagesize) {
+        // when smaller as one page, map to the next power of two
+        newcap--;
+        newcap |= newcap >> 1;
+        newcap |= newcap >> 2;
+        newcap |= newcap >> 4;
+        // last operation only needed for pages larger 4096 bytes
+        // but if/else would be more expensive than just doing this
+        newcap |= newcap >> 8;
+        newcap++;
+    } else {
+        // otherwise, map to a multiple of the page size
+        newcap -= newcap % pagesize;
+        newcap += pagesize;
+        // note: if newcap is already page aligned,
+        // this gives a full additional page (which is good)
+    }
+
+
     const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
     if (buffer->flags & force_copy_flags) {
         void *newspace = cxMalloc(buffer->allocator, newcap);
     const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
     if (buffer->flags & force_copy_flags) {
         void *newspace = cxMalloc(buffer->allocator, newcap);
@@ -204,6 +249,28 @@ int cxBufferMinimumCapacity(
     }
 }
 
     }
 }
 
+void cxBufferShrink(
+        CxBuffer *buffer,
+        size_t reserve
+) {
+    // Ensure buffer is in a reallocatable state
+    const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
+    if (buffer->flags & force_copy_flags) {
+        // do nothing when we are not allowed to reallocate
+        return;
+    }
+
+    // calculate new capacity
+    size_t newCapacity = buffer->size + reserve;
+
+    // If new capacity is smaller than current capacity, resize the buffer
+    if (newCapacity < buffer->capacity) {
+        if (0 == cxReallocate(buffer->allocator, &buffer->bytes, newCapacity)) {
+            buffer->capacity = newCapacity;
+        }
+    }
+}
+
 static size_t cx_buffer_flush_helper(
         const CxBuffer *buffer,
         const unsigned char *src,
 static size_t cx_buffer_flush_helper(
         const CxBuffer *buffer,
         const unsigned char *src,
index 43305803338331518013dda7bce5c77302be9759..e546cae7408b32abfd63c5eecb07cd9d2fcdd2d6 100644 (file)
@@ -474,10 +474,14 @@ bool cxBufferEof(const CxBuffer *buffer);
  *
  * If the current capacity is not sufficient, the buffer will be extended.
  *
  *
  * If the current capacity is not sufficient, the buffer will be extended.
  *
+ * The new capacity will be a power of two until the system's page size is reached.
+ * Then, the new capacity will be a multiple of the page size.
+ *
  * @param buffer the buffer
  * @param capacity the minimum required capacity for this buffer
  * @retval zero the capacity was already sufficient or successfully increased
  * @retval non-zero on allocation failure
  * @param buffer the buffer
  * @param capacity the minimum required capacity for this buffer
  * @retval zero the capacity was already sufficient or successfully increased
  * @retval non-zero on allocation failure
+ * @see cxBufferShrink()
  */
 cx_attr_nonnull
 cx_attr_export
  */
 cx_attr_nonnull
 cx_attr_export
@@ -486,6 +490,29 @@ int cxBufferMinimumCapacity(
         size_t capacity
 );
 
         size_t capacity
 );
 
+/**
+ * Shrinks the capacity of the buffer to fit its current size.
+ *
+ * If @p reserve is larger than zero, the buffer is shrunk to its size plus
+ * the number of reserved bytes.
+ *
+ * If the current capacity is not larger than the size plus the reserved bytes,
+ * nothing happens.
+ *
+ * If the #CX_BUFFER_COPY_ON_WRITE or #CX_BUFFER_COPY_ON_EXTEND flag is set,
+ * this function does nothing.
+ *
+ * @param buffer the buffer
+ * @param reserve the number of bytes that shall remain reserved
+ * @see cxBufferMinimumCapacity()
+ */
+cx_attr_nonnull
+cx_attr_export
+void cxBufferShrink(
+        CxBuffer *buffer,
+        size_t reserve
+);
+
 /**
  * Writes data to a CxBuffer.
  *
 /**
  * Writes data to a CxBuffer.
  *
index b54153d636145117d1b3a8589a642ebcb27dd1af..0fc9e47dec846c76e6cf210972a7f791dfed63d9 100644 (file)
  */
 #define cx_attr_access_w(...) cx_attr_access(__write_only__, __VA_ARGS__)
 
  */
 #define cx_attr_access_w(...) cx_attr_access(__write_only__, __VA_ARGS__)
 
-#if __STDC_VERSION__ >= 202300L
-
-/**
- * Do not warn about unused variable.
- */
-#define cx_attr_unused [[maybe_unused]]
-
-/**
- * Warn about discarded return value.
- */
-#define cx_attr_nodiscard [[nodiscard]]
-
-#else // no C23
-
 /**
  * Do not warn about unused variable.
  */
 /**
  * Do not warn about unused variable.
  */
  */
 #define cx_attr_nodiscard __attribute__((__warn_unused_result__))
 
  */
 #define cx_attr_nodiscard __attribute__((__warn_unused_result__))
 
-#endif // __STDC_VERSION__
-
 
 // ---------------------------------------------------------------------------
 //       MSVC specifics
 
 // ---------------------------------------------------------------------------
 //       MSVC specifics
index a358d02304a2a850c1959e85998885f7d45b8241..c45f9d5a34ec24d9a4ede6c2e59647d9e0e3932a 100644 (file)
@@ -202,6 +202,22 @@ struct cx_list_class_s {
     );
 };
 
     );
 };
 
+/**
+ * Common type for all list implementations.
+ */
+typedef struct cx_list_s CxList;
+
+/**
+ * A shared instance of an empty list.
+ *
+ * Writing to that list is not allowed.
+ *
+ * You can use this is a placeholder for initializing CxList pointers
+ * for which you do not want to reserve memory right from the beginning.
+ */
+cx_attr_export
+extern CxList *const cxEmptyList;
+
 /**
  * Default implementation of an array insert.
  *
 /**
  * Default implementation of an array insert.
  *
@@ -335,11 +351,6 @@ void cx_list_init(
     size_t elem_size
 );
 
     size_t elem_size
 );
 
-/**
- * Common type for all list implementations.
- */
-typedef struct cx_list_s CxList;
-
 /**
  * Returns the number of elements currently stored in the list.
  *
 /**
  * Returns the number of elements currently stored in the list.
  *
@@ -689,6 +700,24 @@ static inline void *cxListAt(
     return list->cl->at(list, index);
 }
 
     return list->cl->at(list, index);
 }
 
+
+/**
+ * Sets the element at the specified index in the list
+ *
+ * @param list the list to set the element in
+ * @param index the index to set the element at
+ * @param elem element to set
+ * @retval zero on success
+ * @retval non-zero when index is out of bounds
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxListSet(
+        CxList *list,
+        size_t index,
+        const void *elem
+);
+
 /**
  * Returns an iterator pointing to the item at the specified index.
  *
 /**
  * Returns an iterator pointing to the item at the specified index.
  *
@@ -773,14 +802,14 @@ CxIterator cxListMutBackwardsIteratorAt(
  *
  * The returned iterator is position-aware.
  *
  *
  * The returned iterator is position-aware.
  *
- * If the list is empty, a past-the-end iterator will be returned.
+ * If the list is empty or @c NULL, a past-the-end iterator will be returned.
  *
  * @param list the list
  * @return a new iterator
  */
  *
  * @param list the list
  * @return a new iterator
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxIterator cxListIterator(const CxList *list) {
 cx_attr_nodiscard
 static inline CxIterator cxListIterator(const CxList *list) {
+    if (list == NULL) list = cxEmptyList;
     return list->cl->iterator(list, 0, false);
 }
 
     return list->cl->iterator(list, 0, false);
 }
 
@@ -789,14 +818,14 @@ static inline CxIterator cxListIterator(const CxList *list) {
  *
  * The returned iterator is position-aware.
  *
  *
  * The returned iterator is position-aware.
  *
- * If the list is empty, a past-the-end iterator will be returned.
+ * If the list is empty or @c NULL, a past-the-end iterator will be returned.
  *
  * @param list the list
  * @return a new iterator
  */
  *
  * @param list the list
  * @return a new iterator
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxIterator cxListMutIterator(CxList *list) {
 cx_attr_nodiscard
 static inline CxIterator cxListMutIterator(CxList *list) {
+    if (list == NULL) list = cxEmptyList;
     return cxListMutIteratorAt(list, 0);
 }
 
     return cxListMutIteratorAt(list, 0);
 }
 
@@ -806,14 +835,14 @@ static inline CxIterator cxListMutIterator(CxList *list) {
  *
  * The returned iterator is position-aware.
  *
  *
  * The returned iterator is position-aware.
  *
- * If the list is empty, a past-the-end iterator will be returned.
+ * If the list is empty or @c NULL, a past-the-end iterator will be returned.
  *
  * @param list the list
  * @return a new iterator
  */
  *
  * @param list the list
  * @return a new iterator
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxIterator cxListBackwardsIterator(const CxList *list) {
 cx_attr_nodiscard
 static inline CxIterator cxListBackwardsIterator(const CxList *list) {
+    if (list == NULL) list = cxEmptyList;
     return list->cl->iterator(list, list->collection.size - 1, true);
 }
 
     return list->cl->iterator(list, list->collection.size - 1, true);
 }
 
@@ -822,14 +851,14 @@ static inline CxIterator cxListBackwardsIterator(const CxList *list) {
  *
  * The returned iterator is position-aware.
  *
  *
  * The returned iterator is position-aware.
  *
- * If the list is empty, a past-the-end iterator will be returned.
+ * If the list is empty or @c NULL, a past-the-end iterator will be returned.
  *
  * @param list the list
  * @return a new iterator
  */
  *
  * @param list the list
  * @return a new iterator
  */
-cx_attr_nonnull
 cx_attr_nodiscard
 static inline CxIterator cxListMutBackwardsIterator(CxList *list) {
 cx_attr_nodiscard
 static inline CxIterator cxListMutBackwardsIterator(CxList *list) {
+    if (list == NULL) list = cxEmptyList;
     return cxListMutBackwardsIteratorAt(list, list->collection.size - 1);
 }
 
     return cxListMutBackwardsIteratorAt(list, list->collection.size - 1);
 }
 
@@ -842,6 +871,7 @@ static inline CxIterator cxListMutBackwardsIterator(CxList *list) {
  * @param elem the element to find
  * @return the index of the element or the size of the list when the element is not found
  * @see cxListIndexValid()
  * @param elem the element to find
  * @return the index of the element or the size of the list when the element is not found
  * @see cxListIndexValid()
+ * @see cxListContains()
  */
 cx_attr_nonnull
 cx_attr_nodiscard
  */
 cx_attr_nonnull
 cx_attr_nodiscard
@@ -852,6 +882,26 @@ static inline size_t cxListFind(
     return list->cl->find_remove((CxList*)list, elem, false);
 }
 
     return list->cl->find_remove((CxList*)list, elem, false);
 }
 
+/**
+ * Checks, if the list contains the specified element.
+ *
+ * The elements are compared with the list's comparator function.
+ *
+ * @param list the list
+ * @param elem the element to find
+ * @retval true if the element is contained
+ * @retval false if the element is not contained
+ * @see cxListFind()
+ */
+cx_attr_nonnull
+cx_attr_nodiscard
+static inline bool cxListContains(
+    const CxList* list,
+    const void* elem
+) {
+    return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size;
+}
+
 /**
  * Checks if the specified index is within bounds.
  *
 /**
  * Checks if the specified index is within bounds.
  *
@@ -943,17 +993,6 @@ int cxListCompare(
 cx_attr_export
 void cxListFree(CxList *list);
 
 cx_attr_export
 void cxListFree(CxList *list);
 
-/**
- * A shared instance of an empty list.
- *
- * Writing to that list is not allowed.
- *
- * You can use this is a placeholder for initializing CxList pointers
- * for which you do not want to reserve memory right from the beginning.
- */
-cx_attr_export
-extern CxList *const cxEmptyList;
-
 
 #ifdef __cplusplus
 } // extern "C"
 
 #ifdef __cplusplus
 } // extern "C"
index fee35d2acb4b424bf0b0b2a8e950c97f2107fd38..5c8ea88731dba84960815fca94fe2a467e62247f 100644 (file)
 #include "common.h"
 #include "allocator.h"
 
 #include "common.h"
 #include "allocator.h"
 
+/** Expands a UCX string as printf arguments. */
+#define CX_SFMT(s) (int) (s).length, (s).ptr
+
+/** Format specifier for a UCX string */
+#define CX_PRIstr ".*s"
+
 /**
  * The maximum length of the "needle" in cx_strstr() that can use SBO.
  */
 /**
  * The maximum length of the "needle" in cx_strstr() that can use SBO.
  */
@@ -333,6 +339,47 @@ void cx_strfree_a(
         cxmutstr *str
 );
 
         cxmutstr *str
 );
 
+/**
+ * Copies a string.
+ *
+ * The memory in the @p dest structure is either allocated or re-allocated to fit the entire
+ * source string, including a zero-terminator.
+ *
+ * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
+ *
+ * @param alloc the allocator
+ * @param dest a pointer to the structure where to copy the contents to
+ * @param src the source string
+ *
+ * @retval zero success
+ * @retval non-zero if re-allocation failed
+ */
+cx_attr_nonnull_arg(1)
+cx_attr_export
+int cx_strcpy_a(
+        const CxAllocator *alloc,
+        cxmutstr *dest,
+        cxstring src
+);
+
+
+/**
+ * Copies a string.
+ *
+ * The memory in the @p dest structure is either allocated or re-allocated to fit the entire
+ * source string, including a zero-terminator.
+ *
+ * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
+ *
+ * @param alloc (@c CxAllocator*) the allocator
+ * @param dest (@c cxmutstr*) a pointer to the structure where to copy the contents to
+ * @param src (@c cxstring) the source string
+ *
+ * @retval zero success
+ * @retval non-zero if re-allocation failed
+ */
+#define cx_strcpy(dest, src) cx_strcpy_a(cxDefaultAllocator, dest, src)
+
 /**
  * Returns the accumulated length of all specified strings.
  * 
 /**
  * Returns the accumulated length of all specified strings.
  * 
index b7f49b5127ed8d2ad6f337127524dfc4e7634377..96866232b8f778198e9b6a5d426a0a6afaf5d17e 100644 (file)
@@ -120,6 +120,7 @@ typedef struct cx_tree_iterator_s {
         size_t stack_size;
         /**
          * The current depth in the tree.
         size_t stack_size;
         /**
          * The current depth in the tree.
+         * The node with which the iteration starts has depth 1.
          */
         size_t depth;
     };
          */
         size_t depth;
     };
@@ -135,6 +136,7 @@ struct cx_tree_visitor_queue_s {
     void *node;
     /**
      * The depth of the node.
     void *node;
     /**
      * The depth of the node.
+     * The first visited node has depth 1.
      */
     size_t depth;
     /**
      */
     size_t depth;
     /**
@@ -1187,6 +1189,18 @@ cx_attr_nodiscard
 cx_attr_export
 size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
 
 cx_attr_export
 size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
 
+/**
+ * Determines the size of the entire tree.
+ *
+ * @param tree the tree
+ * @return the tree size, counting the root as one
+ */
+cx_attr_nonnull
+cx_attr_nodiscard
+static inline size_t cxTreeSize(CxTree *tree) {
+    return tree->size;
+}
+
 /**
  * Determines the depth of the entire tree.
  *
 /**
  * Determines the depth of the entire tree.
  *
index daf6b9ecce5a897aec6a16292b419dcc2ab374a2..497c66e3fb2f4eeb33e695997c8e92c2eb6a89c2 100644 (file)
@@ -494,3 +494,24 @@ void cxListFree(CxList *list) {
     if (list == NULL) return;
     list->cl->deallocate(list);
 }
     if (list == NULL) return;
     list->cl->deallocate(list);
 }
+
+int cxListSet(
+        CxList *list,
+        size_t index,
+        const void *elem
+) {
+    if (index >= list->collection.size) {
+        return 1;
+    }
+
+    if (list->collection.store_pointer) {
+        // For pointer collections, always use climpl
+        void **target = list->climpl->at(list, index);
+        *target = (void *)elem;
+    } else {
+        void *target = list->cl->at(list, index);
+        memcpy(target, elem, list->collection.elem_size);
+    }
+
+    return 0;
+}
index 14b732e6d16623dc7798cb65dc9a02e061d7c0a3..ab3d46613bb44963c218cdafc851de7cb8754f79 100644 (file)
@@ -80,6 +80,22 @@ void cx_strfree_a(
     str->length = 0;
 }
 
     str->length = 0;
 }
 
+int cx_strcpy_a(
+        const CxAllocator *alloc,
+        cxmutstr *dest,
+        cxstring src
+) {
+    if (cxReallocate(alloc, &dest->ptr, src.length + 1)) {
+        return 1;
+    }
+
+    memcpy(dest->ptr, src.ptr, src.length);
+    dest->length = src.length;
+    dest->ptr[dest->length] = '\0';
+
+    return 0;
+}
+
 size_t cx_strlen(
         size_t count,
         ...
 size_t cx_strlen(
         size_t count,
         ...
@@ -106,27 +122,16 @@ cxmutstr cx_strcat_ma(
         ...
 ) {
     if (count == 0) return str;
         ...
 ) {
     if (count == 0) return str;
-
-    cxstring strings_stack[8];
-    cxstring *strings;
-    if (count > 8) {
-        strings = calloc(count, sizeof(cxstring));
-        if (strings == NULL) {
-            return (cxmutstr) {NULL, 0};
-        }
-    } else {
-        strings = strings_stack;
-    }
-
     va_list ap;
     va_start(ap, count);
     va_list ap;
     va_start(ap, count);
+    va_list ap2;
+    va_copy(ap2, ap);
 
 
-    // get all args and overall length
+    // compute overall length
     bool overflow = false;
     size_t slen = str.length;
     for (size_t i = 0; i < count; i++) {
     bool overflow = false;
     size_t slen = str.length;
     for (size_t i = 0; i < count; i++) {
-        cxstring s = va_arg (ap, cxstring);
-        strings[i] = s;
+        cxstring s = va_arg(ap, cxstring);
         if (slen > SIZE_MAX - str.length) overflow = true;
         slen += s.length;
     }
         if (slen > SIZE_MAX - str.length) overflow = true;
         slen += s.length;
     }
@@ -134,10 +139,8 @@ cxmutstr cx_strcat_ma(
 
     // abort in case of overflow
     if (overflow) {
 
     // abort in case of overflow
     if (overflow) {
+        va_end(ap2);
         errno = EOVERFLOW;
         errno = EOVERFLOW;
-        if (strings != strings_stack) {
-            free(strings);
-        }
         return (cxmutstr) { NULL, 0 };
     }
 
         return (cxmutstr) { NULL, 0 };
     }
 
@@ -149,9 +152,7 @@ cxmutstr cx_strcat_ma(
         newstr = cxRealloc(alloc, str.ptr, slen + 1);
     }
     if (newstr == NULL) {
         newstr = cxRealloc(alloc, str.ptr, slen + 1);
     }
     if (newstr == NULL) {
-        if (strings != strings_stack) {
-            free(strings);
-        }
+        va_end(ap2);
         return (cxmutstr) {NULL, 0};
     }
     str.ptr = newstr;
         return (cxmutstr) {NULL, 0};
     }
     str.ptr = newstr;
@@ -160,19 +161,15 @@ cxmutstr cx_strcat_ma(
     size_t pos = str.length;
     str.length = slen;
     for (size_t i = 0; i < count; i++) {
     size_t pos = str.length;
     str.length = slen;
     for (size_t i = 0; i < count; i++) {
-        cxstring s = strings[i];
+        cxstring s = va_arg(ap2, cxstring);
         memcpy(str.ptr + pos, s.ptr, s.length);
         pos += s.length;
     }
         memcpy(str.ptr + pos, s.ptr, s.length);
         pos += s.length;
     }
+    va_end(ap2);
 
     // terminate string
     str.ptr[str.length] = '\0';
 
 
     // terminate string
     str.ptr[str.length] = '\0';
 
-    // free temporary array
-    if (strings != strings_stack) {
-        free(strings);
-    }
-
     return str;
 }
 
     return str;
 }
 
@@ -588,27 +585,6 @@ bool cx_strcasesuffix(
 #endif
 }
 
 #endif
 }
 
-#ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
-#define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
-#endif
-
-struct cx_strreplace_ibuf {
-    size_t *buf;
-    struct cx_strreplace_ibuf *next;
-    unsigned int len;
-};
-
-static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
-    // remember, the first data is on the stack!
-    buf = buf->next;
-    while (buf) {
-        struct cx_strreplace_ibuf *next = buf->next;
-        free(buf->buf);
-        free(buf);
-        buf = next;
-    }
-}
-
 cxmutstr cx_strreplacen_a(
         const CxAllocator *allocator,
         cxstring str,
 cxmutstr cx_strreplacen_a(
         const CxAllocator *allocator,
         cxstring str,
@@ -616,108 +592,60 @@ cxmutstr cx_strreplacen_a(
         cxstring replacement,
         size_t replmax
 ) {
         cxstring replacement,
         size_t replmax
 ) {
-
-    if (search.length == 0 || search.length > str.length || replmax == 0)
+    // special cases
+    if (search.length == 0 || search.length > str.length || replmax == 0) {
         return cx_strdup_a(allocator, str);
         return cx_strdup_a(allocator, str);
-
-    // Compute expected buffer length
-    size_t ibufmax = str.length / search.length;
-    size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
-    if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) {
-        ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE;
     }
 
     }
 
-    // First index buffer can be on the stack
-    struct cx_strreplace_ibuf ibuf, *curbuf = &ibuf;
-    size_t ibuf_sbo[CX_STRREPLACE_INDEX_BUFFER_SIZE];
-    ibuf.buf = ibuf_sbo;
-    ibuf.next = NULL;
-    ibuf.len = 0;
+    size_t in_len = str.length;
+    size_t search_len = search.length;
+    size_t repl_len = replacement.length;
 
 
-    // Search occurrences
-    cxstring searchstr = str;
-    size_t found = 0;
-    do {
-        cxstring match = cx_strstr(searchstr, search);
-        if (match.length > 0) {
-            // Allocate next buffer in chain, if required
-            if (curbuf->len == ibuflen) {
-                struct cx_strreplace_ibuf *nextbuf =
-                        calloc(1, sizeof(struct cx_strreplace_ibuf));
-                if (!nextbuf) {
-                    cx_strrepl_free_ibuf(&ibuf);
-                    return cx_mutstrn(NULL, 0);
-                }
-                nextbuf->buf = calloc(ibuflen, sizeof(size_t));
-                if (!nextbuf->buf) {
-                    free(nextbuf);
-                    cx_strrepl_free_ibuf(&ibuf);
-                    return cx_mutstrn(NULL, 0);
-                }
-                curbuf->next = nextbuf;
-                curbuf = nextbuf;
-            }
-
-            // Record match index
-            found++;
-            size_t idx = match.ptr - str.ptr;
-            curbuf->buf[curbuf->len++] = idx;
-            searchstr.ptr = match.ptr + search.length;
-            searchstr.length = str.length - idx - search.length;
-        } else {
-            break;
-        }
-    } while (searchstr.length > 0 && found < replmax);
-
-    // Allocate result string
-    cxmutstr result;
-    {
-        long long adjlen = (long long) replacement.length - (long long) search.length;
-        size_t rcount = 0;
-        curbuf = &ibuf;
-        do {
-            rcount += curbuf->len;
-            curbuf = curbuf->next;
-        } while (curbuf);
-        result.length = str.length + rcount * adjlen;
-        result.ptr = cxMalloc(allocator, result.length + 1);
-        if (!result.ptr) {
-            cx_strrepl_free_ibuf(&ibuf);
-            return cx_mutstrn(NULL, 0);
-        }
+    // first run, count the occurrences
+    // and remember where the first is
+    size_t occurrences = 1;
+    cxstring first = cx_strstr(str, search);
+    if (first.length == 0) {
+        // special case, no replacements
+        return cx_strdup_a(allocator, str);
+    }
+    cxstring tmp = cx_strsubs(first, search_len);
+    while (occurrences < replmax &&
+            (tmp = cx_strstr(tmp, search)).length > 0) {
+        occurrences++;
+        tmp = cx_strsubs(tmp, search_len);
     }
 
     }
 
-    // Build result string
-    curbuf = &ibuf;
-    size_t srcidx = 0;
-    char *destptr = result.ptr;
-    do {
-        for (size_t i = 0; i < curbuf->len; i++) {
-            // Copy source part up to next match
-            size_t idx = curbuf->buf[i];
-            size_t srclen = idx - srcidx;
-            if (srclen > 0) {
-                memcpy(destptr, str.ptr + srcidx, srclen);
-                destptr += srclen;
-                srcidx += srclen;
-            }
-
-            // Copy the replacement and skip the source pattern
-            srcidx += search.length;
-            memcpy(destptr, replacement.ptr, replacement.length);
-            destptr += replacement.length;
-        }
-        curbuf = curbuf->next;
-    } while (curbuf);
-    memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
-
-    // Result is guaranteed to be zero-terminated
-    result.ptr[result.length] = '\0';
+    // calculate necessary memory
+    signed long long diff_len = (signed long long) repl_len - search_len;
+    size_t out_len = in_len + diff_len * occurrences;
+    cxmutstr out = {
+        cxMalloc(allocator, out_len + 1),
+        out_len
+    };
+    if (out.ptr == NULL) return out;
+
+    // second run: perform the replacements
+    // but start where we found the first occurrence
+    const char *inp = str.ptr;
+    tmp = first;
+    char *outp = out.ptr;
+    while (occurrences-- > 0 && (tmp = cx_strstr(tmp, search)).length > 0) {
+        size_t copylen = tmp.ptr - inp;
+        memcpy(outp, inp, copylen);
+        outp += copylen;
+        memcpy(outp, replacement.ptr, repl_len);
+        outp += repl_len;
+        inp += copylen + search_len;
+        tmp = cx_strsubs(tmp, search_len);
+    }
 
 
-    // Free index buffer
-    cx_strrepl_free_ibuf(&ibuf);
+    // add the remaining string
+    size_t copylen = in_len - (inp - str.ptr);
+    memcpy(outp, inp, copylen);
+    out.ptr[out_len] = '\0';
 
 
-    return result;
+    return out;
 }
 
 CxStrtokCtx cx_strtok_(
 }
 
 CxStrtokCtx cx_strtok_(
index 397f9087ad937e87293f0d07f16d065008ff7c82..73f3b3592496b853ca3b45553c5ce37fd953d0bc 100644 (file)
@@ -226,14 +226,14 @@ int cx_tree_search(
         int ret_elem = sfunc(elem, node);
         if (ret_elem == 0) {
             // if found, exit the search
         int ret_elem = sfunc(elem, node);
         if (ret_elem == 0) {
             // if found, exit the search
-            *result = (void *) elem;
+            *result = elem;
             ret = 0;
             break;
         } else if (ret_elem > 0 && ret_elem < ret) {
             // new distance is better
             *result = elem;
             ret = ret_elem;
             ret = 0;
             break;
         } else if (ret_elem > 0 && ret_elem < ret) {
             // new distance is better
             *result = elem;
             ret = ret_elem;
-        } else {
+        } else if (ret_elem < 0 || ret_elem > ret) {
             // not contained or distance is worse, skip entire subtree
             cxTreeIteratorContinue(iter);
         }
             // not contained or distance is worse, skip entire subtree
             cxTreeIteratorContinue(iter);
         }
@@ -307,10 +307,10 @@ static void cx_tree_iter_next(void *it) {
         // search for the next node
         void *next;
         cx_tree_iter_search_next:
         // search for the next node
         void *next;
         cx_tree_iter_search_next:
-        // check if there is a sibling
+        // check if there is a sibling, but only if we are not a (subtree-)root
         if (iter->exiting) {
             next = iter->node_next;
         if (iter->exiting) {
             next = iter->node_next;
-        } else {
+        } else if (iter->depth > 1) {
             next = tree_next(iter->node);
             iter->node_next = next;
         }
             next = tree_next(iter->node);
             iter->node_next = next;
         }