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 +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
|
}
|