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 +17 -0
- data/INTERNALS +26 -0
- data/README +18 -58
- data/Rakefile +11 -4
- data/THANKS +1 -0
- data/VERSION +1 -1
- data/configure +0 -0
- data/example/compile.sh +3 -0
- data/example/hello +10 -0
- data/example/hello.bin +0 -0
- data/example/hello.c +28 -0
- data/example/hello.rb +8 -0
- data/src/Makefile.in +5 -4
- data/src/lmc_common.c +43 -0
- data/src/lmc_common.h +26 -0
- data/src/lmc_error.c +9 -3
- data/src/lmc_error.h +12 -2
- data/src/lmc_hashtable.c +163 -35
- data/src/lmc_hashtable.h +14 -6
- data/src/lmc_lock.c +69 -11
- data/src/lmc_lock.h +3 -0
- data/src/lmc_shm.c +25 -8
- data/src/lmc_shm.h +1 -0
- data/src/lmc_valloc.c +211 -59
- data/src/lmc_valloc.h +23 -3
- data/src/localmemcache.c +193 -31
- data/src/localmemcache.h +17 -5
- data/src/ruby-binding/localmemcache.rb +13 -3
- data/src/ruby-binding/rblocalmemcache.c +107 -17
- data/src/tests/alloc +0 -0
- data/src/tests/allocfailure +15 -0
- data/src/tests/allocfailure.rb +20 -0
- data/src/tests/bench +0 -0
- data/src/tests/bench.rb +15 -0
- data/src/tests/bench_keys +11 -0
- data/src/tests/bench_keys.rb +24 -0
- data/src/tests/crash +19 -0
- data/src/tests/crash.rb +41 -0
- data/src/tests/lmc +0 -0
- data/src/tests/lmc.rb +22 -47
- data/src/tests/parallelwrite +15 -0
- data/src/tests/parallelwrite.rb +27 -0
- data/src/tests/runtest.sh +0 -0
- data/src/tests/shm +0 -0
- data/src/tests/ttalloc +0 -0
- data/src/tests/ttlmc +0 -0
- data/src/tests/ttlmc.rb +18 -2
- metadata +22 -4
data/.gitignore
ADDED
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
|
-
|
1
|
+
Localmemcache README
|
2
2
|
=====================
|
3
3
|
|
4
|
-
|
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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
27
|
-
|
28
|
-
Ruby's Hash: 4963.313 ms
|
15
|
+
SUPPORTED SYSTEMS
|
16
|
+
=================
|
29
17
|
|
30
|
-
|
31
|
-
|
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 =>
|
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
|
-
|
37
|
+
Ruby binding:
|
49
38
|
|
50
|
-
|
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
|
-
|
78
|
-
=================
|
41
|
+
If you just want to use the C API, download the .tar.gz from:
|
79
42
|
|
80
|
-
|
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 = "
|
51
|
-
"
|
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
|
+
0.2.0
|
data/configure
CHANGED
File without changes
|
data/example/compile.sh
ADDED
data/example/hello
ADDED
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
data/src/Makefile.in
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
CC=@CC@
|
2
2
|
VERSION=@VERSION@
|
3
3
|
TARGET=liblmc.a
|
4
|
-
|
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
|
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,
|
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
|
-
|
17
|
-
|
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 "
|
10
|
+
#include "lmc_common.h"
|
11
11
|
|
12
|
-
|
13
|
-
|
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
|
-
|
16
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
48
|
-
|
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
|
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
|
-
|
56
|
-
|
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
|
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
|
70
|
-
|
71
|
-
|
125
|
+
if (hr == NULL) {
|
126
|
+
LMC_MEMORY_POOL_FULL("ht_set");
|
127
|
+
goto failed;
|
72
128
|
}
|
73
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
163
|
+
size_t va_p = 0;
|
90
164
|
ht_hash_t *ht = base + va_ht;
|
91
|
-
|
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
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
}
|