localmemcache 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }