localmemcache 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.o
2
+ *.a
3
+ core
4
+ vgcore*
5
+ core*
6
+ *.bak
7
+ .*.sw?
8
+ autom4te.cache
9
+ config.status
10
+ *.bin
11
+ ?
12
+ config.log
13
+ lmc_config.h
14
+ Makefile
15
+ *.so
16
+ pkg
17
+
data/INTERNALS ADDED
@@ -0,0 +1,26 @@
1
+ How localmemcache works
2
+ =======================
3
+
4
+ localmemcache is essentially three components:
5
+
6
+ - a wrapper around mmap()
7
+ - an allocator that works with relative memory addresses (replaces malloc(), etc.)
8
+ - hashtable data type
9
+
10
+ Namespace life-cycle
11
+ ====================
12
+
13
+ | $lm = LocalMemCache.new :namespace => :viewcounters #, :size_mb => 1024
14
+
15
+ Namespaces reside as memory-mapped files in /var/tmp/localmemcache. If the
16
+ namespace does not yet exist, it is created with a default size of 1GB. It is
17
+ not possible to resize a namespace, so choose a size that will be enough to
18
+ hold all your data. The class method clear_namespace can be used to delete
19
+ namespaces. You should also use this function in case your namespace becomes
20
+ inconsistent. Note that just deleting the memory-mapped file is not enough.
21
+
22
+ The default size of a namespace is 1GB, it can be changed by passing the option
23
+ :size_mb to the constructor. Note that this is the maximum size of the shared
24
+ memory segment which cannot be extended later without clearing the namespace
25
+ first. Setting large sizes shouldn't be much of a problem since only that
26
+ memory which is really used will exist in physical ram and disk.
data/README CHANGED
@@ -1,34 +1,24 @@
1
- localmemcache README
1
+ Localmemcache README
2
2
  =====================
3
3
 
4
- Efficiently sharing a hashtable between processes on a local Unix machine.
5
-
6
- WHAT IS IT?
7
- ===========
8
-
9
- localmemcache aims to be faster than using memcached locally by using shared
10
- memory, but providing a similar interface.
4
+ The beauty of memcached. For local data. Blazingly fast.
11
5
 
6
+ * http://localmemcache.rubyforge.org/
12
7
 
13
- PERFORMANCE
8
+ WHAT IS IT?
14
9
  ===========
15
- Here's a quick speed comparison, made on an
16
- Intel(R) Xeon(R) CPU E5205 @ 1.86GHz:
17
-
18
- Benchmark pseudo code:
19
10
 
20
- 2_000_000.times {
21
- index = rand(10000).to_s
22
- $hash.set(index, index)
23
- $hash.get(index)
24
- }
11
+ Localmemcache is a library for C and ruby that aims to provide an interface
12
+ similar to memcached but for accessing local data instead of remote data. It's
13
+ based on mmap()'ed shared memory for maximum speed.
25
14
 
26
- MemCache: 253326.122 ms
27
- LocalMemCache: 6055.552 ms
28
- Ruby's Hash: 4963.313 ms
15
+ SUPPORTED SYSTEMS
16
+ =================
29
17
 
30
- So localmemcache is about 40 times faster than using memcache locally, and
31
- about 20% slower than Ruby's hash.
18
+ - Unix (for mmap)
19
+ - A CPU architecture with more than 32 bits is recommended, since otherwise you
20
+ might run out of virtual address space when you use larger shared memory
21
+ segments.
32
22
 
33
23
  EXAMPLE
34
24
  =======
@@ -36,48 +26,18 @@ EXAMPLE
36
26
  In Ruby:
37
27
 
38
28
  require 'localmemcache'
39
- $lm = LocalMemCache.new :namespace => :viewcounters
29
+ $lm = LocalMemCache.new :namespace => "viewcounters"
40
30
  $lm[:foo] = 1
41
31
  $lm[:foo]
42
32
  $lm.delete(:foo)
43
33
 
44
-
45
34
  INSTALLATION
46
35
  ============
47
36
 
48
- # gem install localmemcache
37
+ Ruby binding:
49
38
 
50
- How localmemcache works
51
- =======================
52
-
53
- localmemcache is essentially three components:
54
-
55
- - a wrapper around mmap()
56
- - an allocator that works with relative memory addresses (replaces malloc(), etc.)
57
- - hashtable data type
58
-
59
- Namespace life-cycle
60
- ====================
61
-
62
- | $lm = LocalMemCache.new :namespace => :viewcounters #, :size_mb => 1024
63
-
64
- Namespaces reside as memory-mapped files in /var/tmp/localmemcache. If the
65
- namespace does not yet exist, it is created with a default size of 1GB. It is
66
- not possible to resize a namespace, so choose a size that will be enough to
67
- hold all your data. The class method clear_namespace can be used to delete
68
- namespaces. You should also use this function in case your namespace becomes
69
- inconsistent. Note that just deleting the memory-mapped file is not enough.
70
-
71
- The default size of a namespace is 1GB, it can be changed by passing the option
72
- :size_mb to the constructor. Note that this is the maximum size of the shared
73
- memory segment which cannot be extended later without clearing the namespace
74
- first. Setting large sizes shouldn't be much of a problem since only that
75
- memory which is really used will exist in physical ram and disk.
39
+ # gem install localmemcache
76
40
 
77
- SUPPORTED SYSTEMS
78
- =================
41
+ If you just want to use the C API, download the .tar.gz from:
79
42
 
80
- - Unix (for mmap)
81
- - A CPU architecture with more than 32 bit is recommended, since otherwise you
82
- might run out of virtual address space when you use larger shared memory
83
- segments.
43
+ http://rubyforge.org/frs/?group_id=7925
data/Rakefile CHANGED
@@ -5,13 +5,13 @@ def manifest
5
5
  `git ls-files`.split("\n").select{|n| !%r!/site/!.match(n) }
6
6
  end
7
7
 
8
- def version() File.read("VERSION") end
8
+ def version() File.read("VERSION").chomp end
9
9
 
10
10
  desc "Generate a ChangeLog"
11
11
  task :changelog do
12
12
  File.open("ChangeLog", "w") { |out|
13
13
  `git log -z`.split("\0").map { |chunk|
14
- author = chunk[/Author: (.*)/, 1].strip
14
+ author = chunk[/Author: (.*)/, 1].strip.gsub(/mysites/, "Sven C. Koehler")
15
15
  date = chunk[/Date: (.*)/, 1].strip
16
16
  desc, detail = $'.strip.split("\n", 2)
17
17
  detail ||= ""
@@ -24,6 +24,13 @@ task :changelog do
24
24
  }
25
25
  end
26
26
 
27
+ task :c_api_package do
28
+ tgz = "pkg/localmemcache-#{version}.tar.gz"
29
+ sh "test -d pkg || mkdir pkg"
30
+ puts "Creating #{tgz}"
31
+ sh "tar czf #{tgz.inspect} #{manifest.map{|n| n.inspect }.join(" ")}"
32
+ end
33
+
27
34
  #task :pushsite => [:rdoc] do
28
35
  task :pushsite do
29
36
  sh "chmod 755 site"
@@ -47,8 +54,8 @@ else
47
54
  s.name = "localmemcache"
48
55
  s.version = version
49
56
  s.platform = Gem::Platform::RUBY
50
- s.summary = "Efficiently sharing a hashtable between " +
51
- "processes on a local Unix machine."
57
+ s.summary = "The beauty of memcached. For local data. " +
58
+ "Blazingly fast"
52
59
 
53
60
  s.description = <<-EOF
54
61
 
data/THANKS ADDED
@@ -0,0 +1 @@
1
+ Armin Roehrl: For his thoughts and support
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.2.0
data/configure CHANGED
File without changes
@@ -0,0 +1,3 @@
1
+ #! /bin/sh
2
+
3
+ gcc -o hello.bin hello.c -I ../src/ -L ../src -llmc -lrt
data/example/hello ADDED
@@ -0,0 +1,10 @@
1
+ #! /bin/sh
2
+ D=`dirname $0`
3
+ DIR=`cd $D; pwd`
4
+ script=$DIR/hello.rb
5
+
6
+ if test "x$1" = "x-d"; then
7
+ irb -r $script
8
+ else
9
+ ruby $script
10
+ fi
data/example/hello.bin ADDED
Binary file
data/example/hello.c ADDED
@@ -0,0 +1,28 @@
1
+ #include <stdio.h>
2
+ #include <localmemcache.h>
3
+
4
+ int main() {
5
+ lmc_error_t e;
6
+ local_memcache_t *lmc = local_memcache_create("viewcounters", 0, &e);
7
+ if (!lmc) {
8
+ fprintf(stderr, "Couldn't create localmemcache: %s\n", e.error_str);
9
+ return 1;
10
+ }
11
+ if (!local_memcache_set(lmc, "foo", 3, "1", 1)) goto failed;
12
+ size_t n_value;
13
+ char *value = local_memcache_get_new(lmc, "foo", 3, &n_value);
14
+ if (!value) goto failed;
15
+ free(value);
16
+ if (!local_memcache_delete(lmc, "foo", 3)) goto failed;
17
+ if (!local_memcache_free(lmc, &e)) {
18
+ fprintf(stderr, "Failed to release localmemcache: %s\n", e.error_str);
19
+ return 1;
20
+ }
21
+
22
+ return 0;
23
+
24
+ failed:
25
+ fprintf(stderr, "%s\n", lmc->error.error_str);
26
+ return 1;
27
+
28
+ }
data/example/hello.rb ADDED
@@ -0,0 +1,8 @@
1
+ $DIR=File.dirname(__FILE__)
2
+ ['../src/ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
3
+
4
+ require 'localmemcache'
5
+ $lm = LocalMemCache.new :namespace => "viewcounters"
6
+ $lm[:foo] = 1
7
+ $lm[:foo]
8
+ $lm.delete(:foo)
data/src/Makefile.in CHANGED
@@ -1,7 +1,7 @@
1
1
  CC=@CC@
2
2
  VERSION=@VERSION@
3
3
  TARGET=liblmc.a
4
- CFLAGS=-Wall -Wunused -fPIC -O2
4
+ CFLAGS_ADD=-g -Wall -Wunused -fPIC -O2
5
5
  RUBY_LIB=rblocalmemcache.so
6
6
  LDFLAGS=
7
7
  AR=ar
@@ -14,6 +14,7 @@ OFILES= \
14
14
  lmc_hashtable.o \
15
15
  lmc_shm.o \
16
16
  lmc_lock.o \
17
+ lmc_common.o \
17
18
  localmemcache.o
18
19
 
19
20
  compile: $(TARGET)
@@ -31,7 +32,7 @@ $(TARGET): $(OFILES)
31
32
  $(RANLIB) $@
32
33
 
33
34
  %.o: %.c
34
- $(CC) $(CFLAGS) -c -o $@ $<
35
+ $(CC) $(CFLAGS_ADD) $(CFLAGS) -c -o $@ $<
35
36
 
36
37
  test: $(TARGET)
37
38
  ./tests/runtest.sh
@@ -42,8 +43,8 @@ install: $(TARGET)
42
43
  install -d -m755 ${PREFIX}/include
43
44
  install -m644 ${TARGET} ${PREFIX}/lib
44
45
  @echo INSTALLING headers to ${PREFIX}/include
45
- install -m644 lmc_config.h lmc_error.h lmc_hashtable.h lmc_lock.h \
46
- lmc_shm.h lmc_valloc.h ${PREFIX}/include
46
+ install -m644 lmc_config.h lmc_common.h lmc_error.h lmc_hashtable.h \
47
+ lmc_lock.h lmc_shm.h lmc_valloc.h ${PREFIX}/include
47
48
 
48
49
  clean:
49
50
  rm -f *.log *.so *.a *.o tests/core tests/vgcore* \
data/src/lmc_common.c ADDED
@@ -0,0 +1,43 @@
1
+ /*
2
+ * Copyright (c) 2009, Sven C. Koehler
3
+ */
4
+
5
+ #include "localmemcache.h"
6
+ #include <stdio.h>
7
+ #include <sys/types.h>
8
+ #include <unistd.h>
9
+ #include <stdlib.h>
10
+
11
+ int lmc_test_crash_enabled = 0;
12
+ int lmc_showed_status = 0;
13
+
14
+ void lmc_test_crash(const char* file, int line, const char *function) {
15
+ if (!lmc_showed_status) {
16
+ lmc_showed_status = 1;
17
+ printf("lmc_test_crash enabled\n");
18
+ }
19
+ if (!lmc_test_crash_enabled) { return; }
20
+ int r = rand();
21
+ if ((r & 63) == 0) {
22
+ printf("[%s:%d %s %d] CRASHING: %d\n", file, line, function, getpid(), r);
23
+ char *p = 0x0;
24
+ *p = 0x0;
25
+ }
26
+ }
27
+
28
+ #undef lmc_valloc
29
+
30
+ size_t lmc_test_valloc_fail(const char *file, int line, const char *function,
31
+ void *base, size_t s) {
32
+ if (!lmc_showed_status) {
33
+ lmc_showed_status = 1;
34
+ printf("lmc_test_valloc enabled\n");
35
+ }
36
+ int r = rand();
37
+ if ((r & 255) == 0) {
38
+ printf("[%s:%d %s] valloc fail: %zd\n", file, line, function, s);
39
+ return 0;
40
+ }
41
+ return lmc_valloc(base, s);
42
+ }
43
+
data/src/lmc_common.h ADDED
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Copyright (c) 2009, Sven C. Koehler
3
+ */
4
+
5
+ #ifndef _LMC_COMMON_H_INCLUDED_
6
+ #define _LMC_COMMON_H_INCLUDED_
7
+
8
+ extern int lmc_test_crash_enabled;
9
+ #ifdef DO_TEST_CRASH
10
+ #define LMC_TEST_CRASH lmc_test_crash(__FILE__, __LINE__, __FUNCTION__);
11
+ #else
12
+ #define LMC_TEST_CRASH
13
+ #endif
14
+
15
+ #ifdef DO_TEST_ALLOC_FAILURE
16
+ #define lmc_valloc(base, s) \
17
+ lmc_test_valloc_fail(__FILE__, __LINE__, __FUNCTION__, base, s)
18
+ #endif
19
+
20
+
21
+ void lmc_test_crash(const char* file, int line, const char *function);
22
+ size_t lmc_test_valloc_fail(const char *file, int line, const char *function,
23
+ void *base, size_t s);
24
+
25
+ #endif
26
+
data/src/lmc_error.c CHANGED
@@ -1,18 +1,24 @@
1
+ /*
2
+ * Copyright (c) 2009, Sven C. Koehler
3
+ */
4
+
1
5
  #include "lmc_error.h"
2
6
  #include <stdio.h>
3
7
  #include <stdlib.h>
4
8
  #include <errno.h>
5
9
  #include <string.h>
6
10
 
7
- int lmc_handle_error(int check, const char *ctx, lmc_error_t *e) {
11
+ int lmc_handle_error(int check, const char *ctx, const char *error_type,
12
+ lmc_error_t *e) {
8
13
  if (!check) { return LMC_OK; }
9
- return lmc_handle_error_with_err_string(ctx, strerror(errno), e);
14
+ return lmc_handle_error_with_err_string(ctx, strerror(errno), error_type, e);
10
15
  }
11
16
 
12
17
  int lmc_handle_error_with_err_string(const char *ctx,
13
- const char *error_msg, lmc_error_t *e) {
18
+ const char *error_msg, const char* error_type, lmc_error_t *e) {
14
19
  if (!e) { return LMC_OK; }
15
20
  snprintf((char *)&e->error_str, 1023, "%s: %s", ctx, error_msg);
21
+ snprintf((char *)&e->error_type, 1023, "%s", error_type);
16
22
  e->error_number = errno;
17
23
  return LMC_ERROR;
18
24
  }
data/src/lmc_error.h CHANGED
@@ -10,10 +10,20 @@
10
10
 
11
11
  typedef struct {
12
12
  char error_str[1024];
13
+ char error_type[1024];
13
14
  int error_number;
14
15
  } lmc_error_t;
15
16
 
16
- int lmc_handle_error(int check, const char *ctx, lmc_error_t* e);
17
- int lmc_handle_error_with_err_string(const char *ctx, const char* error_msg,
17
+ #define STD_OUT_OF_MEMORY_ERROR(ctx) \
18
+ lmc_handle_error_with_err_string((ctx), "Out of memory error", \
19
+ "OutOfMemoryError", e)
20
+
21
+ #define LMC_MEMORY_POOL_FULL(ctx) \
22
+ lmc_handle_error_with_err_string((ctx), "Memory pool full", \
23
+ "MemoryPoolFull", e)
24
+
25
+ int lmc_handle_error(int check, const char *ctx, const char *error_type,
18
26
  lmc_error_t* e);
27
+ int lmc_handle_error_with_err_string(const char *ctx, const char *error_msg,
28
+ const char *error_type, lmc_error_t* e);
19
29
  #endif
data/src/lmc_hashtable.c CHANGED
@@ -7,19 +7,30 @@
7
7
  #include <string.h>
8
8
 
9
9
  #include "lmc_hashtable.h"
10
- #include "lmc_valloc.h"
10
+ #include "lmc_common.h"
11
11
 
12
- size_t ht_strdup(void *base, const char *s) {
13
- size_t va_s = lmc_valloc(base, strlen(s) + 1);
12
+ #define LMC_OP_HT_SET 1
13
+
14
+ typedef struct {
15
+ int op_id;
16
+ size_t va_key;
17
+ size_t va_value;
18
+ } lmc_log_ht_set;
19
+
20
+ size_t lmc_ht_strdup(void *base, const char *s, size_t l) {
21
+ size_t va_s = lmc_valloc(base, l + sizeof(size_t) + 1);
14
22
  if (!va_s) { return 0; }
15
- //memcpy(base + va_s, s, strlen(s) + 1);
16
- strcpy(base + va_s, s);
23
+ char *p = base + va_s;
24
+ *(size_t *) p = l;
25
+ p += sizeof(size_t);
26
+ memcpy(p, s, l + 1);
17
27
  return va_s;
18
28
  }
19
29
 
20
- unsigned long ht_hash_key(const char *s) {
30
+ unsigned long ht_hash_key(const char *s, size_t l) {
21
31
  unsigned long v;
22
- for (v = 0; *s != '\0'; s++) { v = *s + 31 * v; }
32
+ size_t i;
33
+ for (v = 0, i = 0; i++ < l; s++) { v = *s + 31 * v; }
23
34
  return v % HT_BUCKETS;
24
35
  }
25
36
 
@@ -28,7 +39,7 @@ ht_hash_entry_t null_node = { 0, 0, 0 };
28
39
  va_ht_hash_t ht_hash_create(void *base, lmc_error_t *e) {
29
40
  va_ht_hash_t va_ht = lmc_valloc(base, sizeof(ht_hash_t));
30
41
  if (!va_ht) {
31
- lmc_handle_error_with_err_string("ht_hash_create", "memory pool full", e);
42
+ LMC_MEMORY_POOL_FULL("ht_hash_create");
32
43
  return 0;
33
44
  }
34
45
  memset(base + va_ht, 0, sizeof(ht_hash_t));
@@ -40,65 +51,182 @@ int ht_hash_destroy(void *base, va_ht_hash_t ht) {
40
51
  return 1;
41
52
  }
42
53
 
43
- ht_hash_entry_t *ht_lookup(void *base, va_ht_hash_t va_ht, const char *key) {
54
+ ht_hash_entry_t *ht_lookup(void *base, va_ht_hash_t va_ht, const char *key,
55
+ size_t n_key) {
44
56
  va_ht_hash_entry_t va_hr;
45
57
  ht_hash_entry_t *hr ;
46
58
  ht_hash_t *ht = base + va_ht;
47
- for (va_hr = ht->va_buckets[ht_hash_key(key)];
48
- va_hr != 0 && hr != NULL; va_hr = hr->va_next) {
59
+ size_t i;
60
+ for (va_hr = ht->va_buckets[ht_hash_key(key, n_key)];
61
+ va_hr != 0 && hr != NULL; ) {
49
62
  hr = va_hr ? base + va_hr : 0;
50
- if (hr && (strcmp(key, base + hr->va_key) == 0)) { return hr; }
63
+ if (!hr) goto next;
64
+ char *s = base + hr->va_key;
65
+ size_t l = *(size_t *) s;
66
+ if (l != n_key) goto next;
67
+ s += sizeof(size_t);
68
+ for (i = 0; i < l; i++) {
69
+ if (s[i] != key[i]) goto next;
70
+ }
71
+ return hr;
72
+
73
+ next:
74
+ va_hr = hr->va_next;
51
75
  }
52
76
  return &null_node;
53
77
  }
54
78
 
55
- char *ht_get(void *base, va_ht_hash_t va_ht, const char *key) {
56
- size_t va = ht_lookup(base, va_ht, key)->va_value;
79
+ ht_hash_entry_t *ht_lookup2(void *base, va_ht_hash_t va_ht, char *k) {
80
+ return ht_lookup(base, va_ht, k + sizeof(size_t), *(size_t *) k);
81
+ }
82
+
83
+ const char *ht_get(void *base, va_ht_hash_t va_ht, const char *key,
84
+ size_t n_key, size_t *n_value) {
85
+ size_t va = ht_lookup(base, va_ht, key, n_key)->va_value;
57
86
  char *r = va ? base + va : 0;
58
- return r;
87
+ if (!r) return 0;
88
+ *n_value = *(size_t *) r;
89
+ return r + sizeof(size_t);
90
+ }
91
+
92
+ size_t lmc_string_len(char *s) { return *(size_t *) s; }
93
+ char *lmc_string_data(char *s) { return s + sizeof(size_t); }
94
+
95
+ int ht_redo(void *base, va_ht_hash_t va_ht, lmc_log_descriptor_t *l,
96
+ lmc_error_t *e) {
97
+ if (l->op_id == LMC_OP_HT_SET) {
98
+ lmc_log_ht_set *l_set = (lmc_log_ht_set *)l;
99
+ if (l_set->va_key == 0 || l_set->va_value == 0) { return 1; }
100
+ char *k = base + l_set->va_key;
101
+ char *v = base + l_set->va_value;
102
+ ht_set(base, va_ht, lmc_string_data(k), lmc_string_len(k),
103
+ lmc_string_data(v), lmc_string_len(v), e);
104
+ return 1;
105
+ }
106
+ return 0;
59
107
  }
60
108
 
61
109
  int ht_set(void *base, va_ht_hash_t va_ht, const char *key,
62
- const char *value, lmc_error_t *e) {
110
+ size_t n_key, const char *value, size_t n_value, lmc_error_t *e) {
63
111
  ht_hash_t *ht = base + va_ht;
64
- ht_hash_entry_t *hr = ht_lookup(base, va_ht, key);
112
+ ht_hash_entry_t *hr = ht_lookup(base, va_ht, key, n_key);
65
113
  unsigned v;
66
114
  if (hr->va_key == 0) {
115
+ int free_key = 1;
116
+ lmc_log_ht_set *l = (lmc_log_ht_set *)lmc_log_op(base, LMC_OP_HT_SET);
117
+ if ((l->va_value = lmc_ht_strdup(base, value, n_value)) == 0 ||
118
+ (l->va_key = lmc_ht_strdup(base, key, n_key)) == 0) {
119
+ LMC_MEMORY_POOL_FULL("ht_set");
120
+ goto failed;
121
+ }
122
+ free_key = 0;
67
123
  va_ht_hash_entry_t va = lmc_valloc(base, sizeof(ht_hash_entry_t));
68
124
  hr = va ? base + va : 0;
69
- if (hr == NULL || (hr->va_key = ht_strdup(base, key)) == 0) {
70
- lmc_handle_error_with_err_string("ht_set", "memory pool full", e);
71
- return 0;
125
+ if (hr == NULL) {
126
+ LMC_MEMORY_POOL_FULL("ht_set");
127
+ goto failed;
72
128
  }
73
- v = ht_hash_key(key);
129
+ LMC_TEST_CRASH
130
+ hr->va_key = l->va_key;
131
+ v = ht_hash_key(key, n_key);
132
+ LMC_TEST_CRASH
74
133
  hr->va_next = ht->va_buckets[v];
75
134
  ht->va_buckets[v] = va;
135
+ hr->va_value = l->va_value;
136
+ if (free_key) {
137
+ size_t va = l->va_key;
138
+ l->op_id = 0;
139
+ LMC_TEST_CRASH
140
+ lmc_free(base, va);
141
+ }
142
+ lmc_log_finish(base);
76
143
  } else {
77
- lmc_free(base, hr->va_value);
78
- }
79
- if ((hr->va_value = ht_strdup(base, value)) == 0) {
80
- lmc_handle_error_with_err_string("ht_set", "memory pool full", e);
81
- return 0;
144
+ LMC_TEST_CRASH
145
+ size_t va = hr->va_value;
146
+ if ((hr->va_value = lmc_ht_strdup(base, value, n_value)) == 0) {
147
+ LMC_MEMORY_POOL_FULL("ht_set");
148
+ goto failed_no_log;
149
+ }
150
+ lmc_free(base, va);
82
151
  }
83
152
  return 1;
153
+
154
+ failed:
155
+ lmc_log_finish(base);
156
+ failed_no_log:
157
+ return 0;
84
158
  }
85
159
 
86
- int ht_delete(void *base, va_ht_hash_t va_ht, const char *key) {
160
+ int ht_delete(void *base, va_ht_hash_t va_ht, const char *key, size_t n_key) {
87
161
  va_ht_hash_entry_t va_hr;
88
162
  ht_hash_entry_t *hr;
89
- ht_hash_entry_t *p = NULL;
163
+ size_t va_p = 0;
90
164
  ht_hash_t *ht = base + va_ht;
91
- unsigned long k = ht_hash_key(key);
165
+ size_t i;
166
+ unsigned long k = ht_hash_key(key, n_key);
92
167
  for (va_hr = ht->va_buckets[k]; va_hr != 0 && hr != NULL;
93
168
  va_hr = hr->va_next) {
94
169
  hr = va_hr ? base + va_hr : 0;
95
- if (hr && (strcmp(key, base + hr->va_key) == 0)) {
96
- // remove previous entry
97
- if (p) { p->va_next = hr->va_next; }
98
- else { ht->va_buckets[k] = 0; }
99
- return 1;
170
+ if (!hr) goto next;
171
+ char *s = base + hr->va_key;
172
+ size_t l = *(size_t *) s;
173
+ if (l != n_key) goto next;
174
+ s += sizeof(size_t);
175
+ for (i = 0; i < l; i++) {
176
+ if (s[i] != key[i]) goto next;
177
+ }
178
+
179
+ ht_hash_entry_t *p = va_p ? base + va_p : 0;
180
+ if (p) { p->va_next = hr->va_next; }
181
+ else { ht->va_buckets[k] = 0; }
182
+ lmc_free(base, hr->va_key);
183
+ lmc_free(base, hr->va_value);
184
+ lmc_free(base, va_hr);
185
+ return 1;
186
+
187
+ next:
188
+ va_p = va_hr;
189
+ }
190
+ return 0;
191
+ }
192
+
193
+ int ht_hash_iterate(void *base, va_ht_hash_t va_ht, void *ctx, ITERATOR_P(iter)) {
194
+ va_ht_hash_entry_t va_hr;
195
+ ht_hash_entry_t *hr;
196
+ ht_hash_t *ht = base + va_ht;
197
+ size_t k;
198
+ for (k = 0; k < HT_BUCKETS; k++) {
199
+ for (va_hr = ht->va_buckets[k]; va_hr != 0 && hr != NULL;
200
+ va_hr = hr->va_next) {
201
+ hr = va_hr ? base + va_hr : 0;
202
+ iter(ctx, base + hr->va_key, base + hr->va_value);
100
203
  }
101
- p = base + hr->va_key;
102
204
  }
205
+ return 1;
206
+ }
207
+
208
+ int ht_check_memory(void *base, va_ht_hash_t va_ht) {
209
+ char *bf = lmc_um_new_mem_usage_bitmap(base);
210
+ if (!bf) return 0;
211
+ va_ht_hash_entry_t va_hr;
212
+ ht_hash_entry_t *hr;
213
+ ht_hash_t *ht = base + va_ht;
214
+ if (!lmc_um_mark_allocated(base, bf, va_ht)) goto failed;
215
+ size_t k;
216
+ for (k = 0; k < HT_BUCKETS; k++) {
217
+ for (va_hr = ht->va_buckets[k]; va_hr != 0 && hr != NULL;
218
+ va_hr = hr->va_next) {
219
+ hr = va_hr ? base + va_hr : 0;
220
+ if (!hr) continue;
221
+ if (!(lmc_um_mark_allocated(base, bf, va_hr) &&
222
+ lmc_um_mark_allocated(base, bf, hr->va_key) &&
223
+ lmc_um_mark_allocated(base, bf, hr->va_value))) goto failed;
224
+ }
225
+ }
226
+ lmc_um_find_leaks(base, bf);
227
+ free(bf);
228
+ return 1;
229
+ failed:
230
+ free(bf);
103
231
  return 0;
104
232
  }