115 changed files with 5587 additions and 599 deletions
-
4.gitignore
-
2Makefile.am.coverage
-
69TODO
-
13assets/html/foo.html
-
14assets/html/main.html
-
0assets/html/robots.txt
-
1assets/js/jquery.js
-
0assets/other/.keep-me
-
184config/mime.types
-
9configure.ac
-
26docs/idea_for_asset_access.md
-
184docs/mime.types
-
245docs/rbdelete.txt
-
235docs/rbinsert.txt
-
53docs/socket_states.txt
-
61include/asset.h
-
1include/cbuf.h
-
23include/cbufpool.h
-
3include/hash/hash.h
-
11include/http/message.h
-
9include/http/response.h
-
4include/http/worker.h
-
48include/http/writer.h
-
1include/socket.h
-
138include/tree.h
-
9include/utils/memory.h
-
31include/utils/mime_type.h
-
4m4/gcov.m4
-
13src/Makefile.am
-
6src/asset/Makefile.am
-
140src/asset/asset.c
-
94src/asset/pool.c
-
2src/auth/Makefile.am
-
8src/auth/credential.c
-
25src/auth/ldap.c
-
303src/binarytree.c
-
4src/cbuf/Makefile.am
-
4src/cbuf/cbuf.c
-
13src/cbuf/read.c
-
10src/cbufpool.c
-
2src/class/Makefile.am
-
12src/class/interface/i_class.c
-
54src/hash.c
-
2src/hash/Makefile.am
-
21src/hash/add.c
-
20src/hash/delete.c
-
10src/hash/each.c
-
19src/hash/get.c
-
9src/hash/hash.c
-
18src/hash/interface/hashable.c
-
8src/hash/value.c
-
3src/http/Makefile.am
-
12src/http/cookie.c
-
8src/http/header.c
-
24src/http/message.c
-
2src/http/parser.c
-
4src/http/parser/p_header.c
-
4src/http/parser/p_request_vars.c
-
28src/http/parser/parse.c
-
10src/http/request.c
-
4src/http/response.c
-
1src/http/response/304.c
-
1src/http/response/403.c
-
3src/http/response/404.c
-
65src/http/response/500.c
-
57src/http/response/asset.c
-
4src/http/response/login_form.c
-
4src/http/response/randval.c
-
4src/http/response/session.c
-
39src/http/worker.c
-
32src/http/worker/get_asset.c
-
85src/http/worker/process.c
-
11src/http/writer.c
-
133src/http/writer/write.c
-
2src/logger/Makefile.am
-
25src/logger/interface/i_logger.c
-
55src/mmapfiletest.c
-
54src/mmapfiletest2.c
-
2src/queue/Makefile.am
-
797src/rbtree.c
-
176src/rbtree2.c
-
2src/server/Makefile.am
-
17src/server/handle_accept.c
-
2src/server/poll.c
-
39src/server/read.c
-
82src/server/run.c
-
57src/server/server.c
-
24src/server/write.c
-
2src/session/Makefile.am
-
4src/session/session.c
-
4src/socket/Makefile.am
-
12src/socket/accept.c
-
32src/socket/nonblock.c
-
2src/stream/Makefile.am
-
26src/stream/read.c
-
25src/stream/write.c
-
12src/taskrambler.c
-
9src/tree/Makefile.am
-
249src/tree/delete.c
-
85src/tree/destroy.c
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="iso-8859-1"?> |
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
|||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
|||
<head> |
|||
<title>This is just foo</title> |
|||
</head> |
|||
<body> |
|||
<h1>A BIG FAT FOO</h1> |
|||
</body> |
|||
</html> |
|||
|
|||
<!-- vim: set ts=4 sw=4: --> |
|||
@ -0,0 +1 @@ |
|||
jquery-1.7.1.min.js |
|||
@ -0,0 +1,184 @@ |
|||
evy application/envoy |
|||
fif application/fractals |
|||
spl application/futuresplash |
|||
hta application/hta |
|||
acx application/internet-property-stream |
|||
hqx application/mac-binhex40 |
|||
doc application/msword |
|||
dot application/msword |
|||
* application/octet-stream |
|||
oda application/oda |
|||
axs application/olescript |
|||
pdf application/pdf |
|||
prf application/pics-rules |
|||
p10 application/pkcs10 |
|||
crl application/pkix-crl |
|||
ai application/postscript |
|||
eps application/postscript |
|||
ps application/postscript |
|||
rtf application/rtf |
|||
setpay application/set-payment-initiation |
|||
setreg application/set-registration-initiation |
|||
xla application/vnd.ms-excel |
|||
xlc application/vnd.ms-excel |
|||
xlm application/vnd.ms-excel |
|||
xls application/vnd.ms-excel |
|||
xlt application/vnd.ms-excel |
|||
xlw application/vnd.ms-excel |
|||
msg application/vnd.ms-outlook |
|||
sst application/vnd.ms-pkicertstore |
|||
cat application/vnd.ms-pkiseccat |
|||
stl application/vnd.ms-pkistl |
|||
pot application/vnd.ms-powerpoint |
|||
pps application/vnd.ms-powerpoint |
|||
ppt application/vnd.ms-powerpoint |
|||
mpp application/vnd.ms-project |
|||
wcm application/vnd.ms-works |
|||
wdb application/vnd.ms-works |
|||
wks application/vnd.ms-works |
|||
wps application/vnd.ms-works |
|||
hlp application/winhlp |
|||
bcpio application/x-bcpio |
|||
cdf application/x-cdf |
|||
z application/x-compress |
|||
tgz application/x-compressed |
|||
cpio application/x-cpio |
|||
csh application/x-csh |
|||
dcr application/x-director |
|||
dir application/x-director |
|||
dxr application/x-director |
|||
dvi application/x-dvi |
|||
gtar application/x-gtar |
|||
gz application/x-gzip |
|||
hdf application/x-hdf |
|||
ins application/x-internet-signup |
|||
isp application/x-internet-signup |
|||
iii application/x-iphone |
|||
js application/x-javascript |
|||
latex application/x-latex |
|||
mdb application/x-msaccess |
|||
crd application/x-mscardfile |
|||
clp application/x-msclip |
|||
dll application/x-msdownload |
|||
m13 application/x-msmediaview |
|||
m14 application/x-msmediaview |
|||
mvb application/x-msmediaview |
|||
wmf application/x-msmetafile |
|||
mny application/x-msmoney |
|||
pub application/x-mspublisher |
|||
scd application/x-msschedule |
|||
trm application/x-msterminal |
|||
wri application/x-mswrite |
|||
nc application/x-netcdf |
|||
pma application/x-perfmon |
|||
pmc application/x-perfmon |
|||
pml application/x-perfmon |
|||
pmr application/x-perfmon |
|||
pmw application/x-perfmon |
|||
p12 application/x-pkcs12 |
|||
pfx application/x-pkcs12 |
|||
p7b application/x-pkcs7-certificates |
|||
spc application/x-pkcs7-certificates |
|||
p7r application/x-pkcs7-certreqresp |
|||
p7c application/x-pkcs7-mime |
|||
p7m application/x-pkcs7-mime |
|||
p7s application/x-pkcs7-signature |
|||
sh application/x-sh |
|||
shar application/x-shar |
|||
swf application/x-shockwave-flash |
|||
sit application/x-stuffit |
|||
sv4cpio application/x-sv4cpio |
|||
sv4crc application/x-sv4crc |
|||
tar application/x-tar |
|||
tcl application/x-tcl |
|||
tex application/x-tex |
|||
texi application/x-texinfo |
|||
texinfo application/x-texinfo |
|||
roff application/x-troff |
|||
t application/x-troff |
|||
tr application/x-troff |
|||
man application/x-troff-man |
|||
me application/x-troff-me |
|||
ms application/x-troff-ms |
|||
ustar application/x-ustar |
|||
src application/x-wais-source |
|||
cer application/x-x509-ca-cert |
|||
crt application/x-x509-ca-cert |
|||
der application/x-x509-ca-cert |
|||
pko application/ynd.ms-pkipko |
|||
zip application/zip |
|||
au audio/basic |
|||
snd audio/basic |
|||
mid audio/mid |
|||
rmi audio/mid |
|||
mp3 audio/mpeg |
|||
aif audio/x-aiff |
|||
aifc audio/x-aiff |
|||
aiff audio/x-aiff |
|||
m3u audio/x-mpegurl |
|||
ra audio/x-pn-realaudio |
|||
ram audio/x-pn-realaudio |
|||
wav audio/x-wav |
|||
bmp image/bmp |
|||
cod image/cis-cod |
|||
gif image/gif |
|||
ief image/ief |
|||
jpe image/jpeg |
|||
jpeg image/jpeg |
|||
jpg image/jpeg |
|||
jfif image/pipeg |
|||
svg image/svg+xml |
|||
tif image/tiff |
|||
tiff image/tiff |
|||
ras image/x-cmu-raster |
|||
cmx image/x-cmx |
|||
ico image/x-icon |
|||
pnm image/x-portable-anymap |
|||
pbm image/x-portable-bitmap |
|||
pgm image/x-portable-graymap |
|||
ppm image/x-portable-pixmap |
|||
rgb image/x-rgb |
|||
xbm image/x-xbitmap |
|||
xpm image/x-xpixmap |
|||
xwd image/x-xwindowdump |
|||
mht message/rfc822 |
|||
mhtml message/rfc822 |
|||
nws message/rfc822 |
|||
css text/css |
|||
323 text/h323 |
|||
htm text/html |
|||
html text/html |
|||
stm text/html |
|||
uls text/iuls |
|||
bas text/plain |
|||
c text/plain |
|||
h text/plain |
|||
txt text/plain |
|||
rtx text/richtext |
|||
sct text/scriptlet |
|||
tsv text/tab-separated-values |
|||
htt text/webviewhtml |
|||
htc text/x-component |
|||
etx text/x-setext |
|||
vcf text/x-vcard |
|||
mp2 video/mpeg |
|||
mpa video/mpeg |
|||
mpe video/mpeg |
|||
mpeg video/mpeg |
|||
mpg video/mpeg |
|||
mpv2 video/mpeg |
|||
mov video/quicktime |
|||
qt video/quicktime |
|||
lsf video/x-la-asf |
|||
lsx video/x-la-asf |
|||
asf video/x-ms-asf |
|||
asr video/x-ms-asf |
|||
asx video/x-ms-asf |
|||
avi video/x-msvideo |
|||
movie video/x-sgi-movie |
|||
flr x-world/x-vrml |
|||
vrml x-world/x-vrml |
|||
wrl x-world/x-vrml |
|||
wrz x-world/x-vrml |
|||
xaf x-world/x-vrml |
|||
xof x-world/x-vrml |
|||
@ -0,0 +1,26 @@ |
|||
The Problem |
|||
=========== |
|||
|
|||
- One asset might be accessed on various positions by multiple workers. |
|||
- The asset might be large. |
|||
- The amount of usable file handles is limited. |
|||
|
|||
Idea |
|||
==== |
|||
|
|||
Create an asset class, wich holds the file handle as well as a memory |
|||
mapping to it, as well as a reference count of interested workers. |
|||
An asset object is created the first time the asset is requested. Then |
|||
the file is opened and memory mapped and the refcount is set to 1. |
|||
The asset is than stored into a hash indexed by the filename. |
|||
The next time the asset is requested it is found in the hash and the |
|||
refcount is increased. |
|||
The asset object might be an observer of each worker it uses, so the |
|||
worker can inform the asset object when they are done, so that the |
|||
reference count can be decreased. |
|||
If the reference count goes to zero, the asset object is removed from the |
|||
hash and freed. |
|||
Each worker in turn has to know how much of the asset is already processed, |
|||
so that it can ask for the position it wants the next data from. |
|||
As each request is assigned one worker and one request can only access one |
|||
asset the worker has to know only one position. |
|||
@ -0,0 +1,184 @@ |
|||
evy application/envoy |
|||
fif application/fractals |
|||
spl application/futuresplash |
|||
hta application/hta |
|||
acx application/internet-property-stream |
|||
hqx application/mac-binhex40 |
|||
doc application/msword |
|||
dot application/msword |
|||
* application/octet-stream |
|||
oda application/oda |
|||
axs application/olescript |
|||
pdf application/pdf |
|||
prf application/pics-rules |
|||
p10 application/pkcs10 |
|||
crl application/pkix-crl |
|||
ai application/postscript |
|||
eps application/postscript |
|||
ps application/postscript |
|||
rtf application/rtf |
|||
setpay application/set-payment-initiation |
|||
setreg application/set-registration-initiation |
|||
xla application/vnd.ms-excel |
|||
xlc application/vnd.ms-excel |
|||
xlm application/vnd.ms-excel |
|||
xls application/vnd.ms-excel |
|||
xlt application/vnd.ms-excel |
|||
xlw application/vnd.ms-excel |
|||
msg application/vnd.ms-outlook |
|||
sst application/vnd.ms-pkicertstore |
|||
cat application/vnd.ms-pkiseccat |
|||
stl application/vnd.ms-pkistl |
|||
pot application/vnd.ms-powerpoint |
|||
pps application/vnd.ms-powerpoint |
|||
ppt application/vnd.ms-powerpoint |
|||
mpp application/vnd.ms-project |
|||
wcm application/vnd.ms-works |
|||
wdb application/vnd.ms-works |
|||
wks application/vnd.ms-works |
|||
wps application/vnd.ms-works |
|||
hlp application/winhlp |
|||
bcpio application/x-bcpio |
|||
cdf application/x-cdf |
|||
z application/x-compress |
|||
tgz application/x-compressed |
|||
cpio application/x-cpio |
|||
csh application/x-csh |
|||
dcr application/x-director |
|||
dir application/x-director |
|||
dxr application/x-director |
|||
dvi application/x-dvi |
|||
gtar application/x-gtar |
|||
gz application/x-gzip |
|||
hdf application/x-hdf |
|||
ins application/x-internet-signup |
|||
isp application/x-internet-signup |
|||
iii application/x-iphone |
|||
js application/x-javascript |
|||
latex application/x-latex |
|||
mdb application/x-msaccess |
|||
crd application/x-mscardfile |
|||
clp application/x-msclip |
|||
dll application/x-msdownload |
|||
m13 application/x-msmediaview |
|||
m14 application/x-msmediaview |
|||
mvb application/x-msmediaview |
|||
wmf application/x-msmetafile |
|||
mny application/x-msmoney |
|||
pub application/x-mspublisher |
|||
scd application/x-msschedule |
|||
trm application/x-msterminal |
|||
wri application/x-mswrite |
|||
nc application/x-netcdf |
|||
pma application/x-perfmon |
|||
pmc application/x-perfmon |
|||
pml application/x-perfmon |
|||
pmr application/x-perfmon |
|||
pmw application/x-perfmon |
|||
p12 application/x-pkcs12 |
|||
pfx application/x-pkcs12 |
|||
p7b application/x-pkcs7-certificates |
|||
spc application/x-pkcs7-certificates |
|||
p7r application/x-pkcs7-certreqresp |
|||
p7c application/x-pkcs7-mime |
|||
p7m application/x-pkcs7-mime |
|||
p7s application/x-pkcs7-signature |
|||
sh application/x-sh |
|||
shar application/x-shar |
|||
swf application/x-shockwave-flash |
|||
sit application/x-stuffit |
|||
sv4cpio application/x-sv4cpio |
|||
sv4crc application/x-sv4crc |
|||
tar application/x-tar |
|||
tcl application/x-tcl |
|||
tex application/x-tex |
|||
texi application/x-texinfo |
|||
texinfo application/x-texinfo |
|||
roff application/x-troff |
|||
t application/x-troff |
|||
tr application/x-troff |
|||
man application/x-troff-man |
|||
me application/x-troff-me |
|||
ms application/x-troff-ms |
|||
ustar application/x-ustar |
|||
src application/x-wais-source |
|||
cer application/x-x509-ca-cert |
|||
crt application/x-x509-ca-cert |
|||
der application/x-x509-ca-cert |
|||
pko application/ynd.ms-pkipko |
|||
zip application/zip |
|||
au audio/basic |
|||
snd audio/basic |
|||
mid audio/mid |
|||
rmi audio/mid |
|||
mp3 audio/mpeg |
|||
aif audio/x-aiff |
|||
aifc audio/x-aiff |
|||
aiff audio/x-aiff |
|||
m3u audio/x-mpegurl |
|||
ra audio/x-pn-realaudio |
|||
ram audio/x-pn-realaudio |
|||
wav audio/x-wav |
|||
bmp image/bmp |
|||
cod image/cis-cod |
|||
gif image/gif |
|||
ief image/ief |
|||
jpe image/jpeg |
|||
jpeg image/jpeg |
|||
jpg image/jpeg |
|||
jfif image/pipeg |
|||
svg image/svg+xml |
|||
tif image/tiff |
|||
tiff image/tiff |
|||
ras image/x-cmu-raster |
|||
cmx image/x-cmx |
|||
ico image/x-icon |
|||
pnm image/x-portable-anymap |
|||
pbm image/x-portable-bitmap |
|||
pgm image/x-portable-graymap |
|||
ppm image/x-portable-pixmap |
|||
rgb image/x-rgb |
|||
xbm image/x-xbitmap |
|||
xpm image/x-xpixmap |
|||
xwd image/x-xwindowdump |
|||
mht message/rfc822 |
|||
mhtml message/rfc822 |
|||
nws message/rfc822 |
|||
css text/css |
|||
323 text/h323 |
|||
htm text/html |
|||
html text/html |
|||
stm text/html |
|||
uls text/iuls |
|||
bas text/plain |
|||
c text/plain |
|||
h text/plain |
|||
txt text/plain |
|||
rtx text/richtext |
|||
sct text/scriptlet |
|||
tsv text/tab-separated-values |
|||
htt text/webviewhtml |
|||
htc text/x-component |
|||
etx text/x-setext |
|||
vcf text/x-vcard |
|||
mp2 video/mpeg |
|||
mpa video/mpeg |
|||
mpe video/mpeg |
|||
mpeg video/mpeg |
|||
mpg video/mpeg |
|||
mpv2 video/mpeg |
|||
mov video/quicktime |
|||
qt video/quicktime |
|||
lsf video/x-la-asf |
|||
lsx video/x-la-asf |
|||
asf video/x-ms-asf |
|||
asr video/x-ms-asf |
|||
asx video/x-ms-asf |
|||
avi video/x-msvideo |
|||
movie video/x-sgi-movie |
|||
flr x-world/x-vrml |
|||
vrml x-world/x-vrml |
|||
wrl x-world/x-vrml |
|||
wrz x-world/x-vrml |
|||
xaf x-world/x-vrml |
|||
xof x-world/x-vrml |
|||
@ -0,0 +1,245 @@ |
|||
Properties of a rbtree |
|||
====================== |
|||
|
|||
1. A node is either red or black. |
|||
2. The root is black. (This rule is sometimes omitted. Since the root can |
|||
always be changed from red to black, but not necessarily vice-versa, |
|||
this rule has little effect on analysis.) |
|||
3. All leaves (NIL) are black. (All leaves are same color as the |
|||
root.) |
|||
4. Every red node must have two black child nodes. |
|||
5. Every simple path from a given node to any of its |
|||
descendant leaves contains the same number of black nodes. |
|||
|
|||
|
|||
Assumptions in addition to wikipedia |
|||
==================================== |
|||
|
|||
lets assume that NULL pointer are black B nodes. But we mark them with a (N) |
|||
|
|||
|
|||
Example |
|||
======= |
|||
|
|||
we start with the tree from the insert example |
|||
---------------------------------------------- |
|||
|
|||
B(11) |
|||
|
|||
R(8) R(13) |
|||
|
|||
B(3) B(9) B(12) B(16) |
|||
|
|||
B(N) B(N) B(N) R(10) B(N) B(N) B(N) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
we remove R(10) / remove a red node (replace with its child): |
|||
wikipedia explains why this can only happen with two leaf children (our B(N)) |
|||
|
|||
B(11) |
|||
|
|||
R(8) R(13) |
|||
|
|||
B(3) B(9) B(12) B(16) |
|||
|
|||
B(N) B(N) B(N) B(N) B(N) B(N) B(N) B(N) |
|||
|
|||
|
|||
again start with the insert example result |
|||
------------------------------------------ |
|||
|
|||
B(11) |
|||
|
|||
R(8) R(13) |
|||
|
|||
B(3) B(9) B(12) B(16) |
|||
|
|||
B(N) B(N) B(N) R(10) B(N) B(N) B(N) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
remove B(9) (which is the second simple case described on wikipedia) |
|||
M black, C red |
|||
After remove just repaint child black. As I do not replace the node, |
|||
but the value this is simplified in my case to simply do nothing after |
|||
normal delete... :D |
|||
|
|||
B(11) |
|||
|
|||
R(8) R(13) |
|||
|
|||
B(3) B(10) B(12) B(16) |
|||
|
|||
B(N) B(N) B(N) B(N) B(N) B(N) B(N) B(N) |
|||
|
|||
|
|||
again start with the insert example result |
|||
------------------------------------------ |
|||
|
|||
B(11) |
|||
|
|||
R(8) R(13) |
|||
|
|||
B(3) B(9) B(12) B(16) |
|||
|
|||
B(N) B(N) B(N) R(10) B(N) B(N) B(N) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
now lets delete B(3)... which is stated on wikipedia as the complicated case |
|||
where 6 subcases could be distinguished. |
|||
|
|||
Wikipedia says we begin with replacing B(3) which one if its childs, in my |
|||
case this means, setting r(8)->left to NULL.... |
|||
|
|||
B(11) |
|||
|
|||
R(8) R(13) |
|||
|
|||
B(N) B(9) B(12) B(16) |
|||
|
|||
B(N) R(10) B(N) B(N) B(N) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
So, what is called in on Wikipedia is simply a nullpointer for me...hopefully |
|||
I don't have to do anything with it. |
|||
|
|||
Get an overview over our variables now: |
|||
|
|||
N : Nullpointer set in R(8)->left (thus N will be black by definition, ever) |
|||
P : R(8) |
|||
S : B(9) |
|||
Sl: Nullpointer |
|||
Sr: R(10) |
|||
|
|||
cases: |
|||
- case 2: S is red => reverse color of P and S, then rotate left at P |
|||
- case 3: P, S and S's children are black => repaint S red |
|||
- case 4: S and S's children are black, P is red => exchange colors of S and |
|||
P |
|||
- case 5: S and Sr black, Sl is red N left of P => rotate right at S, exchange |
|||
colors of S and Sl |
|||
- case 6: S black, Sr is red N left of P => rotate left at P, exchange colors |
|||
of P and S and make Sr black |
|||
|
|||
looks like case 6: |
|||
|
|||
first rotate left at P |
|||
|
|||
B(11) |
|||
|
|||
B(9) R(13) |
|||
|
|||
R(8) R(10) B(12) B(16) |
|||
|
|||
B(N) B(N) B(N) B(N) B(N) B(N) B(N) B(N) |
|||
|
|||
exchange colors of P and S and make Sr black. |
|||
|
|||
B(11) |
|||
|
|||
R(9) R(13) |
|||
|
|||
B(8) B(10) B(12) B(16) |
|||
|
|||
B(N) B(N) B(N) B(N) B(N) B(N) B(N) B(N) |
|||
|
|||
|
|||
|
|||
now for case 2, we delete 16 from the following tree |
|||
|
|||
B(13) |
|||
|
|||
R(8) B(16) |
|||
|
|||
B(3) B(11) B(N) B(N) |
|||
|
|||
B(N) B(N) R(9) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
again first lets see what we have where... |
|||
|
|||
N : Nullpointer set in R(13)->right (thus N will be black by definition, ever) |
|||
P : B(13) |
|||
S : R(8) |
|||
Sl: B(3) |
|||
Sr: B(11) |
|||
|
|||
B(13)P |
|||
|
|||
R(8)S B(N)N |
|||
|
|||
B(3)Sl B(11)Sr |
|||
|
|||
B(N) B(N) R(9) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
revert colors of P and S |
|||
|
|||
R(13)P |
|||
|
|||
B(8)S B(N)N |
|||
|
|||
B(3)Sl B(11)Sr |
|||
|
|||
B(N) B(N) R(9) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
rotate right at P |
|||
|
|||
B(8)S |
|||
|
|||
B(3)Sl R(13)P |
|||
|
|||
B(N) B(N) B(11)Sr B(N)N |
|||
|
|||
R(9) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
relable ... done on wikipedia (don't know if I will need it.) |
|||
|
|||
B(8) |
|||
|
|||
B(3) R(13)P |
|||
|
|||
B(N) B(N) B(11)S B(N)N |
|||
|
|||
R(9)Sl B(N)Sr |
|||
|
|||
B(N) B(N) |
|||
|
|||
ok, not case 3... P is red |
|||
nor is it case 4 Sl is red. |
|||
kind of case 6 reversed ... lets try what they do in the code not in the description... |
|||
|
|||
Sl to black |
|||
|
|||
B(8) |
|||
|
|||
B(3) R(13)P |
|||
|
|||
B(N) B(N) B(11)S B(N)N |
|||
|
|||
B(9)Sl B(N)Sr |
|||
|
|||
B(N) B(N) |
|||
|
|||
rotate right on P |
|||
|
|||
B(8) |
|||
|
|||
B(3) B(11)S |
|||
|
|||
B(N) B(N) B(9)Sl R(13)P |
|||
|
|||
B(N) B(N) B(N)Sr B(N)N |
|||
|
|||
This result is wrong....the balance is ok, but the color of 9 is wrong. |
|||
|
|||
# vim: set et ts=4: |
|||
@ -0,0 +1,235 @@ |
|||
Properties of a rbtree |
|||
====================== |
|||
|
|||
1. A node is either red or black. |
|||
2. The root is black. (This rule is sometimes omitted. Since the root can |
|||
always be changed from red to black, but not necessarily vice-versa, |
|||
this rule has little effect on analysis.) |
|||
3. All leaves (NIL) are black. (All leaves are same color as the |
|||
root.) |
|||
4. Every red node must have two black child nodes. |
|||
5. Every simple path from a given node to any of its |
|||
descendant leaves contains the same number of black nodes. |
|||
|
|||
|
|||
Assumptions in addition to wikipedia |
|||
==================================== |
|||
|
|||
lets assume that NULL pointer are black B nodes. But we mark them with a (N) |
|||
|
|||
|
|||
Example |
|||
======= |
|||
|
|||
we start with an empty tree.... |
|||
------------------------------- |
|||
|
|||
B(N) |
|||
|
|||
|
|||
case 1 (root): add first element... elements are red R wenn added, add key 13. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
R(13) |
|||
|
|||
B(N) B(N) |
|||
|
|||
|
|||
but now we violate rule 2 and correct this simply by changing the root to |
|||
black. => first rule, if we insert into a root node (the root was null) insert |
|||
as black. |
|||
|
|||
B(13) |
|||
|
|||
B(N) B(N) |
|||
|
|||
|
|||
case 2 (black parent): add an element 8. |
|||
----------------------------------------- |
|||
|
|||
B(13) |
|||
|
|||
R(8) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
we violate none of the properties defined. Nothing to be done. |
|||
|
|||
|
|||
Now add 16 which is case 2 again |
|||
|
|||
B(13) |
|||
|
|||
R(8) R(16) |
|||
|
|||
B(N) B(N) B(N) B(N) |
|||
|
|||
|
|||
case 3 (red parent and uncle): add 11.... |
|||
---------------------------------------- |
|||
|
|||
B(13) |
|||
|
|||
R(8) R(16) |
|||
|
|||
B(N) R(11) B(N) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
This violates propert 4 (each red node must have 2 black childs). |
|||
=> repaint both to black and the gandparent to red. |
|||
|
|||
R(13) |
|||
|
|||
B(8) B(16) |
|||
|
|||
B(N) R(11) B(N) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
This violates now property 2 (The root is black) and it may have |
|||
violated property 4 for the gantparent...so we start all over again |
|||
with case 1 on the grantparent. |
|||
As we now might run into case 4 or 5 where also parent uncle and |
|||
grandparent might be needed we should always track |
|||
|
|||
- grandgrandparent |
|||
- granduncle |
|||
- grandparent |
|||
- parent |
|||
- uncle |
|||
|
|||
For now case 1 occurs again wich gives us the following: |
|||
|
|||
B(13) |
|||
|
|||
B(8) B(16) |
|||
|
|||
B(N) R(11) B(N) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
|
|||
Perfect, thats ok again. |
|||
|
|||
Now as preparation of case 4 we add a 3 |
|||
|
|||
B(13) |
|||
|
|||
B(8) B(16) |
|||
|
|||
R(3) R(11) B(N) B(N) |
|||
|
|||
B(N) B(N) B(N) B(N) |
|||
|
|||
|
|||
and now a 9. |
|||
|
|||
B(13) |
|||
|
|||
B(8) B(16) |
|||
|
|||
R(3) R(11) B(N) B(N) |
|||
|
|||
B(N) B(N) R(9) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
|
|||
which again leads to case 3. |
|||
|
|||
B(13) |
|||
|
|||
R(8) B(16) |
|||
|
|||
B(3) B(11) B(N) B(N) |
|||
|
|||
B(N) B(N) R(9) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
and we are fine again. |
|||
now add 12... |
|||
|
|||
B(13) |
|||
|
|||
R(8) B(16) |
|||
|
|||
B(3) B(11) B(N) B(N) |
|||
|
|||
B(N) B(N) R(9) R(12) |
|||
|
|||
B(N) B(N) B(N) B(N) |
|||
|
|||
and now add a 10... |
|||
|
|||
B(13) |
|||
|
|||
R(8) B(16) |
|||
|
|||
B(3) B(11) B(N) B(N) |
|||
|
|||
B(N) B(N) R(9) R(12) |
|||
|
|||
B(N) R(10) B(N) B(N) |
|||
N N |
|||
|
|||
case 3 again... |
|||
|
|||
B(13) |
|||
|
|||
R(8) B(16) |
|||
|
|||
B(3) R(11) B(N) B(N) |
|||
|
|||
B(N) B(N) B(9) B(12) |
|||
|
|||
B(N) R(10) B(N) B(N) |
|||
N N |
|||
|
|||
as case 3 starts again with grandparent as node, which has to be prepared |
|||
before starting over again, we have the following: |
|||
|
|||
node R(11) |
|||
parent R(8) |
|||
uncle B(16) |
|||
grandparent B(13) |
|||
|
|||
|
|||
now property 4 of our new parent is violated which brings us to case 4 as |
|||
it is not the root. (the cases are taken from wikipedia.) |
|||
|
|||
so lets do case 4 on our node (which is left rotate on our parent, |
|||
the parent of our grandparent). |
|||
|
|||
B(13) |
|||
|
|||
R(11) B(16) |
|||
|
|||
R(8) B(12) B(N) B(N) |
|||
|
|||
B(3) B(9) B(N) B(N) |
|||
|
|||
B(N) B(N) B(N) R(10) |
|||
N N |
|||
|
|||
but again we are left in violated state...again property 4 is violated. |
|||
This is always the case, so always do case 5 after case 4. |
|||
|
|||
We can see beside of some subtree changes our node and parent have changed |
|||
role. we need to address this by swaping them in our code. |
|||
|
|||
So, do case 5 (right rotate the grandparent and set grandparent color to red |
|||
and parent color to black. |
|||
|
|||
B(11) |
|||
|
|||
R(8) R(13) |
|||
|
|||
B(3) B(9) B(12) B(16) |
|||
|
|||
B(N) B(N) B(N) R(10) B(N) B(N) B(N) B(N) |
|||
|
|||
B(N) B(N) |
|||
|
|||
# vim: set et ts=4: |
|||
@ -0,0 +1,53 @@ |
|||
each socket can have 4 states while reading or writing...well as far as i can |
|||
say....assuming is is nonblocking. |
|||
These can be checked via errno and return value. |
|||
|
|||
recv send errno |
|||
OK >0 >0 NOT SET |
|||
CLOSED REMOTELY 0 -1 (write)ECONNRESET |
|||
E_AGAIN -1 -1 EAGAIN|EWOULDBLOCK |
|||
ERROR OTHER -1 -1 AMYTHING ELSE |
|||
|
|||
This means we need a slightly different handling for reading and writing. |
|||
It might be neccessary to distinguish other streams from sockets albeit I |
|||
would like to prevent this... |
|||
This might be tested with small test programs. Well, the only special thing |
|||
is ECONNRESET in write mode...because this is something that would not |
|||
happen to files open for writing. |
|||
We need to be more selective when to close the socket. In fact there are |
|||
some errors, as EINTR which does not mean our socket is closed at all. |
|||
|
|||
Error which does not cause us to close the socket while writing: |
|||
|
|||
EAGAIN - go to poll |
|||
EINTR - try again |
|||
ENOBUFS - try again (maybe only a few times....) |
|||
ENOMEM - try again (maybe only a few times....) |
|||
|
|||
All other errors will cause us to close the socket while writing to it. |
|||
|
|||
Error which does not cause us to close the socket while reading: |
|||
|
|||
EAGAIN - go to poll |
|||
EINTR - try again |
|||
ENOMEM - try again (maybe only a few times....) |
|||
|
|||
All other errors will cause us to close the socket while reading from it. |
|||
|
|||
From a server view... |
|||
- read or write a socket as long as OK |
|||
- close if either indicated by read or write...maybe read or write themself |
|||
could do the close. |
|||
- poll if all socket get EAGAIN on read and write. |
|||
|
|||
So...read and write must be able to notify about 3 states at least. |
|||
|
|||
1. I am fine...continue operation on me |
|||
2. I could not continue right now...please poll me |
|||
3. Don't do any further operation on me...please close me. |
|||
|
|||
This could be simply done with return values... |
|||
|
|||
for 1. Return value >= 0 |
|||
for 2. Return -1 STREAM_DO_POLL |
|||
for 3. Return -2 STREAM_DO_CLOSE |
|||
@ -0,0 +1,61 @@ |
|||
/** |
|||
* \file |
|||
* Represents an asset (a file on disk) |
|||
* |
|||
* \author Georg Hopp |
|||
* |
|||
* \copyright |
|||
* Copyright © 2012 Georg Hopp |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef __ASSET_H__ |
|||
#define __ASSET_H__ |
|||
|
|||
#include <sys/types.h> |
|||
|
|||
#include "class.h" |
|||
#include "commons.h" |
|||
#include "hash.h" |
|||
|
|||
|
|||
CLASS(Asset) { |
|||
unsigned long hash; |
|||
|
|||
char fname[2049]; |
|||
char etag[200]; |
|||
char mtime[200]; |
|||
|
|||
size_t nfname; |
|||
size_t netag; |
|||
size_t nmtime; |
|||
|
|||
char * mime_type; |
|||
size_t nmime_type; |
|||
|
|||
int handle; |
|||
char * data; |
|||
size_t size; |
|||
|
|||
size_t ref_count; |
|||
}; |
|||
|
|||
Asset assetPoolGet(const char *, size_t); |
|||
size_t assetPoolRelease(Asset); |
|||
void assetPoolCleanup(void); |
|||
|
|||
#endif // __ASSET_H__ |
|||
|
|||
// vim: set ts=4 sw=4: |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* As the initializations of cbufs is complicated and time consuming |
|||
* with all this shared memory initialization stuff and each cbuf |
|||
* always is the same (at least for this application) we don't free |
|||
* them after they are not used anymore. |
|||
* Instead we store the in this pool and reuse then the next time |
|||
* they are needed. |
|||
* |
|||
* Well thats the idea of this. |
|||
* |
|||
* OK, after review the code...it has been some time since I wrote it, |
|||
* I realize that only one cbuf is used right now. |
|||
* Each connection holds their unprocessed data in another heap reagion |
|||
* and reinitializes the ringbuffer each time with this. |
|||
* |
|||
* This seems both inefficient and error prone. So I will change this. |
|||
* The only question is, how large should our circular buffer be and |
|||
* how many connections do we expect in paralell... |
|||
* |
|||
* We need to have some handling to not accept any more connection |
|||
* when we reached the maximum amount for cbuffers and none is left in |
|||
* the pool. |
|||
*/ |
|||
@ -0,0 +1,138 @@ |
|||
/** |
|||
* \file |
|||
* |
|||
* \author Georg Hopp |
|||
* |
|||
* \copyright |
|||
* Copyright © 2012 Georg Hopp |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef __TREE_H__ |
|||
#define __TREE_H__ |
|||
|
|||
#include "class.h" |
|||
|
|||
#define TREE_RIGHT(node) (NULL!=(node)?(node)->right:NULL) |
|||
#define TREE_LEFT(node) (NULL!=(node)?(node)->left:NULL) |
|||
#define TREE_PARENT(node) (NULL!=(node)?(node)->parent:NULL) |
|||
|
|||
#define TREE_CHILD(node) \ |
|||
(NULL==TREE_RIGHT((node))?TREE_LEFT((node)):TREE_RIGHT((node))) |
|||
|
|||
#define TREE_RIGHT_LEFT(node) \ |
|||
(NULL!=TREE_RIGHT((node))?TREE_LEFT(TREE_RIGHT((node))):NULL) |
|||
|
|||
#define TREE_LEFT_RIGHT(node) \ |
|||
(NULL!=TREE_LEFT((node))?TREE_RIGHT(TREE_LEFT((node))):NULL) |
|||
|
|||
#define TREE_SIBLING(node) \ |
|||
(NULL!=TREE_PARENT((node))? \ |
|||
((node)==TREE_PARENT((node))->left? \ |
|||
TREE_PARENT((node))->right: \ |
|||
TREE_PARENT((node))->left): \ |
|||
NULL) |
|||
|
|||
#define TREE_GRANDPARENT(node) \ |
|||
(NULL!=TREE_PARENT((node))?TREE_PARENT((node))->parent:NULL) |
|||
|
|||
#define TREE_UNCLE(node) \ |
|||
(NULL!=TREE_GRANDPARENT((node))? \ |
|||
(TREE_PARENT((node))==TREE_GRANDPARENT((node))->left? \ |
|||
TREE_GRANDPARENT((node))->right: \ |
|||
TREE_GRANDPARENT((node))->left): \ |
|||
NULL) |
|||
|
|||
#define TREE_ROTATE_LEFT(root, node) \ |
|||
do { \ |
|||
if (NULL != TREE_RIGHT_LEFT((node))) { \ |
|||
TREE_RIGHT_LEFT((node))->parent = (node); \ |
|||
} \ |
|||
TREE_RIGHT((node))->left = (node); \ |
|||
if (NULL != TREE_PARENT((node))) { \ |
|||
if (TREE_PARENT((node))->left==(node)) { \ |
|||
TREE_PARENT((node))->left = (node)->right; \ |
|||
} else { \ |
|||
TREE_PARENT((node))->right = (node)->right; \ |
|||
} \ |
|||
} else { \ |
|||
*(root) = (node)->right; \ |
|||
} \ |
|||
(node)->right = TREE_RIGHT_LEFT((node)); \ |
|||
(node)->parent = (node)->right; \ |
|||
TREE_RIGHT((node))->parent = (node)->parent; \ |
|||
} while(0) |
|||
|
|||
#define TREE_ROTATE_RIGHT(root, node) \ |
|||
do { \ |
|||
if (NULL != TREE_LEFT_RIGHT((node))) { \ |
|||
TREE_LEFT_RIGHT((node))->parent = (node); \ |
|||
} \ |
|||
TREE_LEFT((node))->right = (node); \ |
|||
if (NULL != TREE_PARENT((node))) { \ |
|||
if (TREE_PARENT((node))->left==(node)) { \ |
|||
TREE_PARENT((node))->left = (node)->left; \ |
|||
} else { \ |
|||
TREE_PARENT((node))->right = (node)->left; \ |
|||
} \ |
|||
} else { \ |
|||
*(root) = (node)->left; \ |
|||
} \ |
|||
TREE_LEFT((node))->parent = (node)->parent; \ |
|||
(node)->left = TREE_LEFT_RIGHT((node)); \ |
|||
(node)->parent = (node)->right; \ |
|||
} while(0) |
|||
|
|||
#define TREE_REPLACE_NODE(root, node1, node2) \ |
|||
do { \ |
|||
if (NULL != TREE_PARENT((node1))) { \ |
|||
if ((node1) == TREE_PARENT((node1))->left) { \ |
|||
TREE_PARENT((node1))->left = (node2); \ |
|||
} else { \ |
|||
TREE_PARENT((node1))->right = (node2); \ |
|||
} \ |
|||
} else { \ |
|||
*(root) = (node2); \ |
|||
} \ |
|||
if (NULL != (node2)) { \ |
|||
(node2)->parent = (node1)->parent; \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
|
|||
enum rbColor {rbBlack=1, rbRed=2}; |
|||
|
|||
CLASS(Tree) { |
|||
void * data; |
|||
|
|||
enum rbColor color; |
|||
|
|||
Tree parent; |
|||
Tree left; |
|||
Tree right; |
|||
}; |
|||
|
|||
typedef int (*TreeComp)(const void *, const void *); |
|||
typedef void (*TreeAction)(const void *, const int); |
|||
|
|||
void * treeFind(Tree, const void *, TreeComp); |
|||
void * treeInsert(Tree *, const void *, TreeComp); |
|||
void * treeDelete(Tree *, const void *, TreeComp); |
|||
void treeWalk(Tree, TreeAction); |
|||
void treeDestroy(Tree *, TreeAction); |
|||
|
|||
#endif // __TREE_H__ |
|||
|
|||
// vim: set ts=4 sw=4: |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* \file |
|||
* |
|||
* \author Georg Hopp |
|||
* |
|||
* \copyright |
|||
* Copyright © 2012 Georg Hopp |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef __UTILS_MIME_TYPE_H__ |
|||
#define __UTILS_MIME_TYPE_H__ |
|||
|
|||
char * getMimeType(const char *, size_t); |
|||
void clearMimeTypes(void); |
|||
|
|||
#endif // __UTILS_MIME_TYPE_H__ |
|||
|
|||
// vim: set ts=4 sw=4: |
|||
@ -0,0 +1,6 @@ |
|||
ACLOCAL_AMFLAGS = -I m4 |
|||
|
|||
noinst_LIBRARIES = libasset.a |
|||
|
|||
libasset_a_SOURCES = asset.c pool.c |
|||
libasset_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/ |
|||
@ -0,0 +1,140 @@ |
|||
/** |
|||
* \file |
|||
* |
|||
* \author Georg Hopp |
|||
* |
|||
* \copyright |
|||
* Copyright © 2012 Georg Hopp |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#include <stdarg.h> |
|||
|
|||
// for mmap |
|||
#include <sys/mman.h> |
|||
|
|||
// for open and fstat |
|||
#include <sys/types.h> |
|||
#include <sys/stat.h> |
|||
#include <fcntl.h> |
|||
|
|||
// for access |
|||
#include <unistd.h> |
|||
|
|||
// for localtime |
|||
#include <time.h> |
|||
|
|||
|
|||
#include "class.h" |
|||
#include "asset.h" |
|||
#include "hash.h" |
|||
|
|||
#include "utils/mime_type.h" |
|||
#include "utils/hash.h" |
|||
|
|||
|
|||
static |
|||
int |
|||
assetCtor(void * _this, va_list * params) |
|||
{ |
|||
Asset this = _this; |
|||
|
|||
struct tm * tmp; |
|||
struct stat st; |
|||
char * fname = va_arg(*params, char*); |
|||
char * ext; |
|||
|
|||
this->nfname = va_arg(*params, size_t); |
|||
|
|||
strncpy(this->fname, fname, 2048); |
|||
this->fname[2048] = '\0'; |
|||
|
|||
this->hash = sdbm( |
|||
(unsigned char *)this->fname, |
|||
this->nfname); |
|||
|
|||
if (-1 == access(this->fname, O_RDONLY)) { |
|||
return -1; |
|||
} else { |
|||
this->handle = open(this->fname, O_RDONLY); |
|||
fstat(this->handle, &st); |
|||
} |
|||
|
|||
tmp = localtime(&(st.st_mtime)); |
|||
this->netag = strftime(this->etag, sizeof(this->etag), "%s", tmp); |
|||
this->nmtime = strftime( |
|||
this->mtime, sizeof(this->mtime), "%a, %d %b %Y %T %Z", tmp); |
|||
|
|||
this->size = st.st_size; |
|||
|
|||
ext = strrchr(this->fname, '.'); |
|||
if (NULL != ext) { |
|||
ext++; |
|||
this->mime_type = getMimeType(ext, strlen(ext)); |
|||
} else { |
|||
this->mime_type = "application/octet-stream"; |
|||
} |
|||
|
|||
if (NULL != this->mime_type) { |
|||
this->nmime_type = strlen(this->mime_type); |
|||
} else { |
|||
this->nmime_type = 0; |
|||
} |
|||
|
|||
this->data = mmap( |
|||
NULL, this->size, PROT_READ, MAP_PRIVATE, this->handle, 0); |
|||
|
|||
if (MAP_FAILED == this->data) { |
|||
close(this->handle); |
|||
return -1; |
|||
} |
|||
|
|||
this->ref_count = 1; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static void assetDtor(void * _this) { |
|||
Asset this = _this; |
|||
|
|||
if (MAP_FAILED != this->data && NULL != this->data) { |
|||
munmap(this->data, this->size); |
|||
} |
|||
|
|||
if (0 < this->handle) { |
|||
close(this->handle); |
|||
} |
|||
} |
|||
|
|||
static |
|||
unsigned long |
|||
assetGetHash(void * _this) |
|||
{ |
|||
Asset this = _this; |
|||
|
|||
return this->hash; |
|||
} |
|||
|
|||
static |
|||
void |
|||
assetHandleDouble(void * _this, void * _doub) |
|||
{ |
|||
} |
|||
|
|||
INIT_IFACE(Class, assetCtor, assetDtor, NULL); |
|||
INIT_IFACE(Hashable, assetGetHash, assetHandleDouble); |
|||
CREATE_CLASS(Asset, NULL, IFACE(Class), IFACE(Hashable)); |
|||
|
|||
// vim: set ts=4 sw=4: |
|||
@ -0,0 +1,94 @@ |
|||
/** |
|||
* \file |
|||
* |
|||
* \author Georg Hopp |
|||
* |
|||
* \copyright |
|||
* Copyright © 2012 Georg Hopp |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
// for size_t |
|||
#include <sys/types.h> |
|||
|
|||
// for strlen |
|||
|
|||
#include "class.h" |
|||
#include "asset.h" |
|||
#include "hash.h" |
|||
|
|||
Hash asset_pool = NULL; |
|||
|
|||
static |
|||
inline |
|||
void |
|||
freeAsset(const void * _node) |
|||
{ |
|||
delete(_node); |
|||
} |
|||
|
|||
Asset |
|||
assetPoolGet(const char * path, size_t npath) |
|||
{ |
|||
Asset asset = NULL; |
|||
|
|||
if (NULL == asset_pool) { |
|||
asset_pool = new(Hash); |
|||
} else { |
|||
asset = hashGet(asset_pool, path, npath); |
|||
} |
|||
|
|||
if (NULL == asset) { |
|||
asset = new(Asset, path, npath); |
|||
hashAdd(asset_pool, asset); |
|||
} else { |
|||
asset->ref_count++; |
|||
} |
|||
|
|||
return asset; |
|||
} |
|||
|
|||
size_t |
|||
assetPoolRelease(Asset asset) |
|||
{ |
|||
if (asset->ref_count > 1) { |
|||
asset->ref_count--; |
|||
return asset->ref_count; |
|||
} |
|||
|
|||
if (NULL != asset) { |
|||
Asset found = (Asset)hashDelete( |
|||
asset_pool, asset->fname, asset->nfname); |
|||
|
|||
if (found == asset) { |
|||
delete(found); |
|||
} else { |
|||
// this should never happen....error log... |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
void |
|||
assetPoolCleanup(void) |
|||
{ |
|||
if (NULL != asset_pool) { |
|||
hashEach(asset_pool, freeAsset); |
|||
delete(asset_pool); |
|||
} |
|||
} |
|||
|
|||
// vim: set ts=4 sw=4: |
|||
@ -0,0 +1,303 @@ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
|
|||
|
|||
struct element |
|||
{ |
|||
int data; |
|||
|
|||
struct element * parent; |
|||
struct element * left; |
|||
struct element * right; |
|||
}; |
|||
|
|||
struct element * |
|||
newElement(int data) |
|||
{ |
|||
struct element * el = malloc(sizeof(struct element)); |
|||
el->data = data; |
|||
el->parent = NULL; |
|||
el->left = NULL; |
|||
el->right = NULL; |
|||
|
|||
return el; |
|||
} |
|||
|
|||
/** |
|||
* find element in tree |
|||
*/ |
|||
struct element * |
|||
findElement(struct element * tree, int data) |
|||
{ |
|||
while (NULL != tree) { |
|||
if (tree->data == data) { |
|||
break; |
|||
} |
|||
|
|||
if (data < tree->data) { |
|||
tree = tree->left; |
|||
} else { |
|||
tree = tree->right; |
|||
} |
|||
} |
|||
|
|||
return tree; |
|||
} |
|||
|
|||
/** |
|||
* insert element in tree |
|||
*/ |
|||
void |
|||
insertElement(struct element ** tree, int data) |
|||
{ |
|||
struct element * node = *tree; |
|||
|
|||
if (NULL == node) { |
|||
*tree = newElement(data); |
|||
return; |
|||
} |
|||
|
|||
while (data != node->data) { |
|||
if (data < node->data) { |
|||
if (NULL == node->left) { |
|||
node->left = newElement(data); |
|||
node->left->parent = node; |
|||
return; |
|||
} else { |
|||
node = node->left; |
|||
} |
|||
} else { |
|||
if (NULL == node->right) { |
|||
node->right = newElement(data); |
|||
node->right->parent = node; |
|||
return; |
|||
} else { |
|||
node = node->right; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* delete element from tree |
|||
* here multiple functions are involved.... |
|||
* ======================================================================= |
|||
*/ |
|||
/** |
|||
* find minimum of the right subtree aka leftmost leaf of right subtree |
|||
* aka left in-order successor. |
|||
* We return the parent of the element in the out argument parent. |
|||
* This can be NULL wenn calling. |
|||
*/ |
|||
struct element * |
|||
findInOrderSuccessor(struct element * tree) |
|||
{ |
|||
struct element * node = tree->right; |
|||
|
|||
while (NULL != node->left) { |
|||
node = node->left; |
|||
} |
|||
|
|||
return node; |
|||
} |
|||
|
|||
void |
|||
deleteElement(struct element ** tree, int data) |
|||
{ |
|||
struct element * node = *tree; |
|||
|
|||
// find the relevant node and it's parent |
|||
while (NULL != node && node->data != data) { |
|||
if (data < node->data) { |
|||
node = node->left; |
|||
} else { |
|||
node = node->right; |
|||
} |
|||
} |
|||
|
|||
// element not found |
|||
if (NULL == node) { |
|||
return; |
|||
} |
|||
|
|||
// distinuish 3 cases, where the resolving of each case leads to the |
|||
// precondition of the other. |
|||
|
|||
// case 1: two children |
|||
if (NULL != node->left && NULL != node->right) { |
|||
struct element * successor = findInOrderSuccessor(node); |
|||
|
|||
node->data = successor->data; |
|||
node = successor; |
|||
} |
|||
|
|||
// case 2: one child wither left or right |
|||
if (NULL != node->left) { |
|||
//node->data = node->left->data; |
|||
//node = node->left; |
|||
if (NULL != node->parent) { |
|||
if (node == node->parent->left) { |
|||
node->parent->left = node->left; |
|||
} else { |
|||
node->parent->right = node->left; |
|||
} |
|||
} |
|||
node->left->parent = node->parent; |
|||
} |
|||
|
|||
if (NULL != node->right) { |
|||
//node->data = node->right->data; |
|||
//node = node->right; |
|||
if (NULL != node->parent) { |
|||
if (node == node->parent->left) { |
|||
node->parent->left = node->right; |
|||
} else { |
|||
node->parent->right = node->right; |
|||
} |
|||
} |
|||
node->right->parent = node->parent; |
|||
} |
|||
|
|||
// case 3: we are a leaf |
|||
if (NULL != node->parent) { |
|||
if (node == node->parent->left) { |
|||
node->parent->left = NULL; |
|||
} else { |
|||
node->parent->right = NULL; |
|||
} |
|||
} |
|||
|
|||
if (node == *tree) { |
|||
if (NULL != node->left) { |
|||
*tree = node->left; |
|||
} else if (NULL != node->right) { |
|||
*tree = node->right; |
|||
} else { |
|||
*tree = NULL; |
|||
} |
|||
} |
|||
|
|||
free(node); |
|||
} |
|||
|
|||
|
|||
void |
|||
traverse(struct element * tree, void (*cb)(int, int)) |
|||
{ |
|||
struct element * previous = tree; |
|||
struct element * node = tree; |
|||
int depth = 1; |
|||
|
|||
/* |
|||
* I think this has something like O(n+log(n)) on a ballanced |
|||
* tree because I have to traverse back the rightmost leaf to |
|||
* the root to get a break condition. |
|||
*/ |
|||
while (node) { |
|||
/* |
|||
* If we come from the right so nothing and go to our |
|||
* next parent. |
|||
*/ |
|||
if (previous == node->right) { |
|||
previous = node; |
|||
node = node->parent; |
|||
depth--; |
|||
continue; |
|||
} |
|||
|
|||
if ((NULL == node->left || previous == node->left)) { |
|||
/* |
|||
* If there are no more elements to the left or we |
|||
* came from the left, process data. |
|||
*/ |
|||
cb(node->data, depth); |
|||
previous = node; |
|||
|
|||
if (NULL != node->right) { |
|||
node = node->right; |
|||
depth++; |
|||
} else { |
|||
node = node->parent; |
|||
depth--; |
|||
} |
|||
} else { |
|||
/* |
|||
* if there are more elements to the left go there. |
|||
*/ |
|||
previous = node; |
|||
node = node->left; |
|||
depth++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void printElement(int data, int depth) |
|||
{ |
|||
int i; |
|||
|
|||
printf("%02d(%02d)", data, depth); |
|||
for (i=0; i<depth; i++) printf("-"); |
|||
puts(""); |
|||
} |
|||
|
|||
/** |
|||
* ======================================================================= |
|||
*/ |
|||
int |
|||
main(int argc, char * argv[]) |
|||
{ |
|||
struct element * root = NULL; |
|||
|
|||
insertElement(&root, 13); |
|||
insertElement(&root, 8); |
|||
insertElement(&root, 16); |
|||
insertElement(&root, 11); |
|||
insertElement(&root, 3); |
|||
insertElement(&root, 9); |
|||
insertElement(&root, 12); |
|||
insertElement(&root, 10); |
|||
|
|||
/* |
|||
* delete does not work correctly here.. |
|||
* luckily I do not need the simple binary trees anymore |
|||
* as I have rbtrees. |
|||
*/ |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
deleteElement(&root, 8); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
deleteElement(&root, 11); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
deleteElement(&root, 13); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
deleteElement(&root, 3); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
deleteElement(&root, 16); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
deleteElement(&root, 10); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
deleteElement(&root, 9); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
deleteElement(&root, 12); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
// vim: set et ts=4 sw=4: |
|||
@ -0,0 +1,10 @@ |
|||
/** |
|||
* As the initializations of cbufs is complicated and time consuming |
|||
* with all this shared memory initialization stuff and each cbuf |
|||
* always is the same (at least for this application) we don't free |
|||
* them after they are not used anymore. |
|||
* Instead we store the in this pool and reuse then the next time |
|||
* they are needed. |
|||
* |
|||
* Well thats the idea of this. |
|||
*/ |
|||
@ -0,0 +1,54 @@ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
|
|||
#include "class.h" |
|||
#include "commons.h" |
|||
#include "utils/memory.h" |
|||
|
|||
#include "hash.h" |
|||
#include "utils/memory.h" |
|||
|
|||
|
|||
/** |
|||
* ======================================================================= |
|||
*/ |
|||
int |
|||
main(int argc, char * argv[]) |
|||
{ |
|||
Hash hash = new(Hash); |
|||
HashValue deleted; |
|||
|
|||
hashAdd(hash, new(HashValue, CSTRA("foo"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("hjkfdd"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("j8frheff"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("f9e0g"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("gfrk9e0"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("fr09ie"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("fu8de9"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("rehw78"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("fcrne9"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("new8"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("fdhe78"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("dhew8"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("jfde9w8"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("dhe7w89"), CSTRA("bar"))); |
|||
hashAdd(hash, new(HashValue, CSTRA("fduew89"), CSTRA("bar"))); |
|||
|
|||
deleted = hashDelete(hash, CSTRA("f9e0g")); |
|||
delete(deleted); |
|||
deleted = hashDelete(hash, CSTRA("fcrne9")); |
|||
delete(deleted); |
|||
deleted = hashDelete(hash, CSTRA("fr09ie")); |
|||
delete(deleted); |
|||
deleted = hashDelete(hash, CSTRA("jfde9w8")); |
|||
delete(deleted); |
|||
deleted = hashDelete(hash, CSTRA("j8frheff")); |
|||
delete(deleted); |
|||
|
|||
delete(hash); |
|||
memCleanup(); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
// vim: set et ts=4 sw=4: |
|||
@ -0,0 +1,65 @@ |
|||
/** |
|||
* \file |
|||
* |
|||
* \author Georg Hopp |
|||
* |
|||
* \copyright |
|||
* Copyright © 2012 Georg Hopp |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <stdio.h> |
|||
#include <sys/types.h> |
|||
|
|||
#include "class.h" |
|||
|
|||
#include "http/response.h" |
|||
#include "http/message.h" |
|||
#include "http/header.h" |
|||
|
|||
#include "utils/memory.h" |
|||
#include "hash.h" |
|||
|
|||
#define RESP_DATA "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" \ |
|||
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" \ |
|||
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" \ |
|||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" \ |
|||
"<head><title>500 - Internal Server Error</title></head>" \ |
|||
"<body><h1>500 - Internal Server Error</h1></body>" \ |
|||
"</html>" |
|||
|
|||
|
|||
HttpResponse |
|||
httpResponse500() |
|||
{ |
|||
HttpResponse response; |
|||
HttpMessage message; |
|||
|
|||
response = new(HttpResponse, "HTTP/1.1", 500, "Internal Server Error"); |
|||
message = (HttpMessage)response; |
|||
|
|||
hashAdd(message->header, |
|||
new(HttpHeader, CSTRA("Content-Type"), CSTRA("text/html"))); |
|||
|
|||
message->nbody = sizeof(RESP_DATA) - 1; |
|||
message->body = memMalloc(sizeof(RESP_DATA)); |
|||
memcpy(message->body, RESP_DATA, sizeof(RESP_DATA)); |
|||
|
|||
return response; |
|||
} |
|||
|
|||
// vim: set ts=4 sw=4: |
|||
@ -0,0 +1,55 @@ |
|||
// for mmap |
|||
#include <sys/mman.h> |
|||
|
|||
// for random |
|||
#include <stdlib.h> |
|||
|
|||
// for open and fstat |
|||
#include <sys/types.h> |
|||
#include <sys/stat.h> |
|||
#include <fcntl.h> |
|||
|
|||
// for puts |
|||
#include <stdio.h> |
|||
|
|||
// for time |
|||
#include <time.h> |
|||
|
|||
int |
|||
main(int argc, char * argv[]) |
|||
{ |
|||
struct stat st; |
|||
char * map; |
|||
size_t position; |
|||
char print_buf[101]; |
|||
int i; |
|||
|
|||
print_buf[100] = '\0'; |
|||
|
|||
int fd = open("./mmapfiletest.c", O_RDONLY); |
|||
|
|||
fstat(fd, &st); |
|||
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
|||
|
|||
srandom(time(NULL)); |
|||
position = random() % (st.st_size - 100); |
|||
|
|||
for (i=0; i<100; i+=10) { |
|||
print_buf[i+0] = map[position + i + 0]; |
|||
print_buf[i+1] = map[position + i + 1]; |
|||
print_buf[i+2] = map[position + i + 2]; |
|||
print_buf[i+3] = map[position + i + 3]; |
|||
print_buf[i+4] = map[position + i + 4]; |
|||
print_buf[i+5] = map[position + i + 5]; |
|||
print_buf[i+6] = map[position + i + 6]; |
|||
print_buf[i+7] = map[position + i + 7]; |
|||
print_buf[i+8] = map[position + i + 8]; |
|||
print_buf[i+9] = map[position + i + 9]; |
|||
} |
|||
|
|||
puts(print_buf); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
// vim: set et ts=4 sw=4: |
|||
@ -0,0 +1,54 @@ |
|||
// for random |
|||
#include <stdlib.h> |
|||
|
|||
// for puts |
|||
#include <stdio.h> |
|||
|
|||
// for time |
|||
#include <time.h> |
|||
|
|||
#include "class.h" |
|||
#include "commons.h" |
|||
#include "utils/memory.h" |
|||
|
|||
#include "asset.h" |
|||
|
|||
int |
|||
main(int argc, char * argv[]) |
|||
{ |
|||
size_t i; |
|||
size_t position; |
|||
char print_buf[101]; |
|||
|
|||
Asset asset = new(Asset, CSTRA("./src/mmapfiletest.c")); |
|||
|
|||
print_buf[100] = '\0'; |
|||
|
|||
srandom(time(NULL)); |
|||
position = random() % (asset->size - 100); |
|||
|
|||
for (i=0; i<100; i+=10) { |
|||
print_buf[i+0] = asset->data[position+i+0]; |
|||
print_buf[i+1] = asset->data[position+i+1]; |
|||
print_buf[i+2] = asset->data[position+i+2]; |
|||
print_buf[i+3] = asset->data[position+i+3]; |
|||
print_buf[i+4] = asset->data[position+i+4]; |
|||
print_buf[i+5] = asset->data[position+i+5]; |
|||
print_buf[i+6] = asset->data[position+i+6]; |
|||
print_buf[i+7] = asset->data[position+i+7]; |
|||
print_buf[i+8] = asset->data[position+i+8]; |
|||
print_buf[i+9] = asset->data[position+i+9]; |
|||
} |
|||
|
|||
if (NULL != asset->mime_type) { |
|||
puts(asset->mime_type); |
|||
} |
|||
puts(print_buf); |
|||
|
|||
delete(asset); |
|||
memCleanup(); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
// vim: set et ts=4 sw=4: |
|||
@ -0,0 +1,797 @@ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
|
|||
#define NVALUES 10 |
|||
|
|||
enum rbColor {rbBlack=1, rbRed=2}; |
|||
|
|||
|
|||
struct element |
|||
{ |
|||
size_t size; |
|||
void * ptr; |
|||
|
|||
enum rbColor color; |
|||
|
|||
struct element * next; |
|||
struct element * last; |
|||
|
|||
struct element * parent; |
|||
struct element * left; |
|||
struct element * right; |
|||
}; |
|||
|
|||
struct element * |
|||
newElement(size_t size) |
|||
{ |
|||
struct element * element = malloc(size + sizeof(struct element)); |
|||
|
|||
element->size = size; |
|||
element->ptr = element + sizeof(struct element); |
|||
|
|||
element->next = NULL; |
|||
element->last = NULL; |
|||
|
|||
element->color = rbRed; |
|||
element->parent = NULL; |
|||
element->left = NULL; |
|||
element->right = NULL; |
|||
|
|||
return element; |
|||
} |
|||
|
|||
/** |
|||
* find element in tree |
|||
*/ |
|||
struct element * |
|||
findElement(struct element * tree, size_t size) |
|||
{ |
|||
struct element * fitting = NULL; |
|||
|
|||
while (NULL != tree) { |
|||
if (tree->size == size) { |
|||
fitting = tree; |
|||
break; |
|||
} |
|||
|
|||
if (size > tree->size) { |
|||
tree = tree->right; |
|||
} else { |
|||
fitting = tree; |
|||
tree = tree->left; |
|||
} |
|||
} |
|||
|
|||
return fitting; |
|||
} |
|||
|
|||
/* |
|||
* function to get specific elements needed for |
|||
* rb handling, grandparent, uncle and sibbling |
|||
*/ |
|||
struct element * |
|||
grandparent(struct element * node) |
|||
{ |
|||
if (NULL != node && NULL != node->parent) { |
|||
return node->parent->parent; |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
struct element * |
|||
uncle(struct element * node) |
|||
{ |
|||
struct element * gp = grandparent(node); |
|||
|
|||
if (NULL == gp) { |
|||
return NULL; |
|||
} |
|||
|
|||
if (node->parent == gp->left) { |
|||
return gp->right; |
|||
} |
|||
|
|||
return gp->left; |
|||
} |
|||
|
|||
struct element * |
|||
sibling(struct element * node) |
|||
{ |
|||
if (NULL == node) { |
|||
return NULL; |
|||
} |
|||
|
|||
if (NULL == node->parent->left || node == node->parent->left) { |
|||
return node->parent->right; |
|||
} else { |
|||
return node->parent->left; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* tree modifications...needed for rb handling. |
|||
*/ |
|||
void |
|||
rotateLeft(struct element ** tree, struct element * node) |
|||
{ |
|||
struct element * rightChild = node->right; |
|||
struct element * rcLeftSub = node->right->left; |
|||
|
|||
rightChild->left = node; |
|||
rightChild->parent = node->parent; |
|||
node->right = rcLeftSub; |
|||
if (NULL != rcLeftSub) { |
|||
rcLeftSub->parent = node; |
|||
} |
|||
|
|||
if (node->parent) { |
|||
if (node->parent->left == node) { |
|||
node->parent->left = rightChild; |
|||
} else { |
|||
node->parent->right = rightChild; |
|||
} |
|||
} else { |
|||
*tree = rightChild; |
|||
} |
|||
|
|||
node->parent = rightChild; |
|||
} |
|||
|
|||
void |
|||
rotateRight(struct element ** tree, struct element * node) |
|||
{ |
|||
struct element * leftChild = node->left; |
|||
struct element * lcRightSub = node->left->right; |
|||
|
|||
leftChild->right = node; |
|||
leftChild->parent = node->parent; |
|||
node->left = lcRightSub; |
|||
if (NULL != lcRightSub) { |
|||
lcRightSub->parent = node; |
|||
} |
|||
|
|||
if (node->parent) { |
|||
if (node->parent->left == node) { |
|||
node->parent->left = leftChild; |
|||
} else { |
|||
node->parent->right = leftChild; |
|||
} |
|||
} else { |
|||
*tree = leftChild; |
|||
} |
|||
|
|||
node->parent = leftChild; |
|||
} |
|||
|
|||
void |
|||
replaceNode( |
|||
struct element ** tree, |
|||
struct element * node1, |
|||
struct element * node2) |
|||
{ |
|||
if (NULL != node1->parent) { |
|||
if (node1 == node1->parent->left) { |
|||
node1->parent->left = node2; |
|||
} else { |
|||
node1->parent->right = node2; |
|||
} |
|||
} else { |
|||
*tree = node2; |
|||
} |
|||
|
|||
if (NULL != node2) { |
|||
node2->parent = node1->parent; |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* insert element in tree |
|||
*/ |
|||
struct element * |
|||
insertElement(struct element ** tree, struct element * element) |
|||
{ |
|||
struct element * node = *tree; |
|||
struct element * new_node = NULL; |
|||
struct element * u; |
|||
struct element * g; |
|||
|
|||
element->next = NULL; |
|||
element->last = NULL; |
|||
|
|||
element->color = rbRed; |
|||
element->parent = NULL; |
|||
element->left = NULL; |
|||
element->right = NULL; |
|||
|
|||
// if tree is empty it's simple... :) |
|||
if (NULL == node) { |
|||
*tree = node = new_node = element; |
|||
} else { |
|||
// normal binary tree add.... |
|||
while (NULL != node) { |
|||
if (element->size < node->size) { |
|||
if (NULL == node->left) { |
|||
node->left = element; |
|||
node->left->parent = node; |
|||
new_node = node = node->left; |
|||
break; |
|||
} else { |
|||
node = node->left; |
|||
} |
|||
} else if (element->size > node->size) { |
|||
if (NULL == node->right) { |
|||
node->right = element; |
|||
node->right->parent = node; |
|||
new_node = node = node->right; |
|||
break; |
|||
} else { |
|||
node = node->right; |
|||
} |
|||
} else { |
|||
if (NULL == node->next) { |
|||
node->next = element; |
|||
node->last = element; |
|||
} else { |
|||
node->last->next = element; |
|||
node->last = element; |
|||
} |
|||
return node; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (NULL != new_node) { |
|||
/* |
|||
* handle reballancing rb style |
|||
*/ |
|||
while (1) { |
|||
// case 1 |
|||
if (node->parent == NULL) { |
|||
node->color = rbBlack; |
|||
// we're done.... :) |
|||
break; |
|||
} |
|||
|
|||
// case 2 |
|||
if (node->parent->color == rbBlack) { |
|||
// Tree is still valid ... wow, again we're done... :) |
|||
break; |
|||
} |
|||
|
|||
// case 3 |
|||
u = uncle(node); |
|||
g = grandparent(node); |
|||
|
|||
if (u != NULL && u->color == rbRed) { |
|||
node->parent->color = rbBlack; |
|||
u->color = rbBlack; |
|||
g->color = rbRed; |
|||
|
|||
node = g; |
|||
continue; |
|||
} |
|||
|
|||
// case 4 |
|||
if (node == node->parent->right && node->parent == g->left) { |
|||
rotateLeft(tree, node->parent); |
|||
node = node->left; |
|||
} else if (node == node->parent->left && node->parent == g->right) { |
|||
|
|||
rotateRight(tree, node->parent); |
|||
node = node->right; |
|||
} |
|||
|
|||
// case 5 |
|||
g = grandparent(node); |
|||
|
|||
node->parent->color = rbBlack; |
|||
g->color = rbRed; |
|||
|
|||
if (node == node->parent->left) { |
|||
rotateRight(tree, g); |
|||
} else { |
|||
rotateLeft(tree, g); |
|||
} |
|||
|
|||
// we're done.. |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return new_node; |
|||
} |
|||
|
|||
/** |
|||
* delete element from tree |
|||
* here multiple functions are involved.... |
|||
* ======================================================================= |
|||
*/ |
|||
/** |
|||
* find minimum of the right subtree aka leftmost leaf of right subtree |
|||
* aka left in-order successor. |
|||
* We return the parent of the element in the out argument parent. |
|||
* This can be NULL wenn calling. |
|||
* |
|||
* 2: *successor = {size = 80, ptr = 0x603ae0, color = rbRed, parent = 0x603160, |
|||
* left = 0x0, right = 0x0} |
|||
* 1: *node = {size = 70, ptr = 0x603a60, color = rbBlack, parent = 0x603070, |
|||
* left = 0x6030e0, right = 0x6031e0} |
|||
* |
|||
*/ |
|||
struct element * |
|||
findInOrderSuccessor(struct element * tree) |
|||
{ |
|||
struct element * node = tree->right; |
|||
|
|||
while (NULL != node->left) { |
|||
node = node->left; |
|||
} |
|||
|
|||
return node; |
|||
} |
|||
|
|||
struct element * deleteOneChild(struct element **, struct element *); |
|||
|
|||
struct element * |
|||
deleteElement(struct element ** tree, struct element * element) |
|||
{ |
|||
struct element * node = *tree; |
|||
struct element * del_node; |
|||
struct element * child; |
|||
struct element * s; |
|||
|
|||
// find the relevant node and it's parent |
|||
while (NULL != node) { |
|||
|
|||
if (element->size < node->size) { |
|||
node = node->left; |
|||
} else if (element->size > node->size) { |
|||
node = node->right; |
|||
} else { |
|||
if (NULL != node->next) { |
|||
if (NULL != node->parent) { |
|||
if (node == node->parent->left) { |
|||
node->parent->left = node->next; |
|||
} else { |
|||
node->parent->right = node->next; |
|||
} |
|||
} else { |
|||
*tree = node->next; |
|||
} |
|||
|
|||
if (NULL != node->left) { |
|||
node->left->parent = node->next; |
|||
} |
|||
|
|||
if (NULL != node->right) { |
|||
node->right->parent = node->next; |
|||
} |
|||
|
|||
node->next->last = node->last; |
|||
node->next->color = node->color; |
|||
node->next->parent = node->parent; |
|||
node->next->left = node->left; |
|||
node->next->right = node->right; |
|||
|
|||
return node; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// element not found |
|||
if (NULL == node) { |
|||
return node; |
|||
} |
|||
|
|||
del_node = node; |
|||
|
|||
// now our cases follows...the first one is the same as with |
|||
// simple binary search trees. Two non null children. |
|||
|
|||
// case 1: two children |
|||
if (NULL != node->left && NULL != node->right) { |
|||
struct element * successor = findInOrderSuccessor(node); |
|||
|
|||
enum rbColor tmpcolor = successor->color; |
|||
struct element * tmpparent = successor->parent; |
|||
struct element * tmpleft = successor->left; |
|||
struct element * tmpright = successor->right; |
|||
|
|||
replaceNode(tree, node, successor); |
|||
|
|||
successor->color = node->color; |
|||
successor->left = node->left; |
|||
successor->left->parent = successor; |
|||
// the right one might be successor... |
|||
if (node->right == successor) { |
|||
successor->right = node; |
|||
node->parent = successor; |
|||
} else { |
|||
successor->right = node->right; |
|||
node->right->parent = successor; |
|||
node->parent = tmpparent; |
|||
tmpparent->left = node; |
|||
} |
|||
|
|||
node->color = tmpcolor; |
|||
node->left = tmpleft; |
|||
node->right = tmpright; |
|||
} |
|||
|
|||
// Precondition: n has at most one non-null child. |
|||
child = (NULL == node->right) ? node->left : node->right; |
|||
replaceNode(tree, node, child); |
|||
|
|||
// delete one child case |
|||
// TODO this is overly complex as simply derived from the function... |
|||
// maybe this can be simplified. Maybe not...check. |
|||
if (node->color == rbBlack) { |
|||
if (NULL != child && child->color == rbRed) { |
|||
child->color = rbBlack; |
|||
// done despite modifying tree itself if neccessary.. |
|||
return del_node; |
|||
} else { |
|||
if (NULL != child) { |
|||
node = child; |
|||
} else { |
|||
node->color = rbBlack; |
|||
node->left = NULL; |
|||
node->right = NULL; |
|||
} |
|||
} |
|||
} else { |
|||
return del_node; |
|||
} |
|||
|
|||
// delete and rb rebalance... |
|||
while(1) { |
|||
// case 1 |
|||
if (NULL == node->parent) { |
|||
// done again |
|||
break; |
|||
} |
|||
|
|||
// case 2 |
|||
s = sibling(node); |
|||
|
|||
if (NULL != s && s->color == rbRed) { |
|||
node->parent->color = rbRed; |
|||
s->color = rbBlack; |
|||
|
|||
/* |
|||
* detect which child we are...assumption |
|||
* if we are not parent->right and parent->right is not |
|||
* null we must be left, even if its set to NULL previously |
|||
*/ |
|||
if (NULL != node->parent->right && node != node->parent->right) { |
|||
rotateLeft(tree, node->parent); |
|||
} else { |
|||
rotateRight(tree, node->parent); |
|||
} |
|||
} |
|||
|
|||
s = sibling(node); |
|||
// case 3 / 4 |
|||
if (NULL == s || ((s->color == rbBlack) && |
|||
(NULL == s->left || s->left->color == rbBlack) && |
|||
(NULL == s->right || s->right->color == rbBlack))) { |
|||
|
|||
if (NULL != s) { |
|||
s->color = rbRed; |
|||
} |
|||
|
|||
if (node->parent->color == rbBlack) { |
|||
// case 3 |
|||
node = node->parent; |
|||
continue; |
|||
} else { |
|||
// case 4 |
|||
node->parent->color = rbBlack; |
|||
// and done again... |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// case 5 |
|||
if (NULL != s && s->color == rbBlack) { |
|||
// this if statement is trivial, |
|||
// due to case 2 (even though case 2 changed the sibling to a |
|||
// sibling's child, |
|||
// the sibling's child can't be red, since no red parent can |
|||
// have a red child). |
|||
// |
|||
// the following statements just force the red to be on the |
|||
// left of the left of the parent, |
|||
// or right of the right, so case 6 will rotate correctly. |
|||
if ((node == node->parent->left) && |
|||
(NULL == s->right || s->right->color == rbBlack) && |
|||
(NULL != s->left && s->left->color == rbRed)) { |
|||
|
|||
// this last test is trivial too due to cases 2-4. |
|||
s->color = rbRed; |
|||
s->left->color = rbBlack; |
|||
|
|||
rotateRight(tree, s); |
|||
} else if ((node == node->parent->right) && |
|||
(NULL == s->left || s->left->color == rbBlack) && |
|||
(NULL != s->right && s->right->color == rbRed)) { |
|||
// this last test is trivial too due to cases 2-4. |
|||
s->color = rbRed; |
|||
s->right->color = rbBlack; |
|||
|
|||
rotateLeft(tree, s); |
|||
} |
|||
} |
|||
|
|||
s = sibling(node); |
|||
// case 6 |
|||
if (NULL != s) { |
|||
s->color = node->parent->color; |
|||
} |
|||
|
|||
if (NULL != node && NULL != node->parent) { |
|||
node->parent->color = rbBlack; |
|||
|
|||
/* |
|||
* detect which child we are...assumption |
|||
* if we are not parent->right and parent->right is not |
|||
* null we must be left, even if its set to NULL previously |
|||
*/ |
|||
if (NULL != node->parent->right && node != node->parent->right) { |
|||
if (NULL != s->right) { |
|||
s->right->color = rbBlack; |
|||
} |
|||
rotateLeft(tree, node->parent); |
|||
} else { |
|||
if (NULL != s->left) { |
|||
s->left->color = rbBlack; |
|||
} |
|||
rotateRight(tree, node->parent); |
|||
} |
|||
} |
|||
|
|||
// done... |
|||
break; |
|||
} |
|||
|
|||
//deleteOneChild(tree, node); |
|||
|
|||
return del_node; |
|||
} |
|||
|
|||
|
|||
void |
|||
traverse(struct element * tree, void (*cb)(struct element *, int)) |
|||
{ |
|||
struct element * previous = tree; |
|||
struct element * node = tree; |
|||
int depth = 1; |
|||
|
|||
/* |
|||
* I think this has something like O(n+log(n)) on a ballanced |
|||
* tree because I have to traverse back the rightmost leaf to |
|||
* the root to get a break condition. |
|||
*/ |
|||
while (node) { |
|||
/* |
|||
* If we come from the right so nothing and go to our |
|||
* next parent. |
|||
*/ |
|||
if (previous == node->right) { |
|||
previous = node; |
|||
node = node->parent; |
|||
depth--; |
|||
continue; |
|||
} |
|||
|
|||
if ((NULL == node->left || previous == node->left)) { |
|||
/* |
|||
* If there are no more elements to the left or we |
|||
* came from the left, process data. |
|||
*/ |
|||
cb(node, depth); |
|||
previous = node; |
|||
|
|||
if (NULL != node->right) { |
|||
node = node->right; |
|||
depth++; |
|||
} else { |
|||
node = node->parent; |
|||
depth--; |
|||
} |
|||
} else { |
|||
/* |
|||
* if there are more elements to the left go there. |
|||
*/ |
|||
previous = node; |
|||
node = node->left; |
|||
depth++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void |
|||
post(struct element * tree, void (*cb)(struct element *, int)) |
|||
{ |
|||
struct element * previous = tree; |
|||
struct element * node = tree; |
|||
int depth = 1; |
|||
|
|||
/* |
|||
* I think this has something like O(n+log(n)) on a ballanced |
|||
* tree because I have to traverse back the rightmost leaf to |
|||
* the root to get a break condition. |
|||
*/ |
|||
while (node) { |
|||
/* |
|||
* If we come from the right so nothing and go to our |
|||
* next parent. |
|||
*/ |
|||
if ((NULL == node->left && NULL == node->right) |
|||
|| previous == node->right) { |
|||
|
|||
struct element * parent = node->parent; |
|||
|
|||
cb(node, depth); |
|||
|
|||
previous = node; |
|||
node = parent; |
|||
depth--; |
|||
continue; |
|||
} |
|||
|
|||
if ((NULL == node->left || previous == node->left)) { |
|||
/* |
|||
* If there are no more elements to the left or we |
|||
* came from the left, process data. |
|||
*/ |
|||
previous = node; |
|||
|
|||
if (NULL != node->right) { |
|||
node = node->right; |
|||
depth++; |
|||
} else { |
|||
node = node->parent; |
|||
depth--; |
|||
} |
|||
} else { |
|||
/* |
|||
* if there are more elements to the left go there. |
|||
*/ |
|||
previous = node; |
|||
node = node->left; |
|||
depth++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void printElement(struct element * node, int depth) |
|||
{ |
|||
int i; |
|||
|
|||
printf("%s %010zu:%p(%02d)", |
|||
(node->color==rbRed)?"R":"B", |
|||
node->size, |
|||
node->ptr, |
|||
depth); |
|||
for (i=0; i<depth; i++) printf("-"); |
|||
puts(""); |
|||
|
|||
node = node->next; |
|||
while (NULL != node) { |
|||
printf(" %s %010zu:%p(%02d)", |
|||
(node->color==rbRed)?"R":"B", |
|||
node->size, |
|||
node->ptr, |
|||
depth); |
|||
for (i=0; i<depth; i++) printf("-"); |
|||
puts(""); |
|||
node = node->next; |
|||
} |
|||
} |
|||
|
|||
void cleanup(struct element * node, int depth) |
|||
{ |
|||
while (NULL != node) { |
|||
printf("free node: "); |
|||
printElement(node, 0); |
|||
struct element * next = node->next; |
|||
free(node); |
|||
node = next; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* ======================================================================= |
|||
*/ |
|||
int |
|||
main(int argc, char * argv[]) |
|||
{ |
|||
struct element * root = NULL; |
|||
struct element * found = NULL; |
|||
|
|||
insertElement(&root, newElement(40)); |
|||
insertElement(&root, newElement(50)); |
|||
insertElement(&root, newElement(60)); |
|||
insertElement(&root, newElement(70)); |
|||
insertElement(&root, newElement(80)); |
|||
insertElement(&root, newElement(45)); |
|||
insertElement(&root, newElement(75)); |
|||
insertElement(&root, newElement(85)); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
puts(""); |
|||
|
|||
insertElement(&root, newElement(70)); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
puts(""); |
|||
|
|||
found = findElement(root, 10); |
|||
if (NULL == found) { |
|||
printf("can't find segmenet of minimum size: %d\n", 10); |
|||
} else { |
|||
printElement(found, 0); |
|||
} |
|||
puts(""); |
|||
|
|||
found = findElement(root, 64); |
|||
if (NULL == found) { |
|||
printf("can't find segmenet of minimum size: %d\n", 64); |
|||
} else { |
|||
printElement(found, 0); |
|||
} |
|||
puts(""); |
|||
|
|||
found = findElement(root, 90); |
|||
if (NULL == found) { |
|||
printf("can't find segmenet of minimum size: %d\n", 90); |
|||
} else { |
|||
printElement(found, 0); |
|||
} |
|||
puts(""); |
|||
|
|||
free(deleteElement(&root, findElement(root, 70))); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
puts(""); |
|||
|
|||
insertElement(&root, newElement(80)); |
|||
insertElement(&root, newElement(50)); |
|||
insertElement(&root, newElement(80)); |
|||
|
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
puts(""); |
|||
|
|||
found = deleteElement(&root, findElement(root, 80)); |
|||
printf("up to free: %p\n", found); |
|||
free(found); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
puts(""); |
|||
|
|||
found = deleteElement(&root, findElement(root, 50)); |
|||
printf("up to free: %p\n", found); |
|||
free(found); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
puts(""); |
|||
|
|||
found = deleteElement(&root, findElement(root, 70)); |
|||
printf("up to free: %p\n", found); |
|||
free(found); |
|||
puts("traverse"); |
|||
traverse(root, printElement); |
|||
puts(""); |
|||
|
|||
// post(root, cleanup); |
|||
// |
|||
return 0; |
|||
} |
|||
|
|||
// vim: set et ts=4 sw=4: |
|||
@ -0,0 +1,176 @@ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
|
|||
#include "class.h" |
|||
#include "commons.h" |
|||
#include "utils/memory.h" |
|||
|
|||
#include "tree.h" |
|||
#include "utils/memory.h" |
|||
|
|||
#define NVALUES 10 |
|||
|
|||
int |
|||
insertCompare(const void * tval, const void * search) |
|||
{ |
|||
return *(const int *)tval - *(const int *)search; |
|||
} |
|||
|
|||
void |
|||
freeNode(const void * data, const int depth) |
|||
{ |
|||
printf("now free %d at %p\n", *(int*)data, data); |
|||
MEM_FREE(data); |
|||
} |
|||
|
|||
void |
|||
printNode(const void * _node, const int depth) |
|||
{ |
|||
Tree node = (Tree)_node; |
|||
int value = *(int *)node->data; |
|||
int i; |
|||
|
|||
for (i=1; i<7; i++) i<=depth?printf("-"):printf(" "); |
|||
printf("%p:%d p:%p l:%p r:%p\n", |
|||
node, value, node->parent, node->left, node->right); |
|||
|
|||
// printf("%s %010d(%02d)", |
|||
// (node->color==rbRed)?"R":"B", |
|||
// value, |
|||
// depth); |
|||
} |
|||
|
|||
|
|||
void * |
|||
newEle(int value) |
|||
{ |
|||
void * val = memMalloc(sizeof(int)); |
|||
|
|||
*(int*)val = value; |
|||
return val; |
|||
} |
|||
|
|||
/** |
|||
* ======================================================================= |
|||
*/ |
|||
int |
|||
main(int argc, char * argv[]) |
|||
{ |
|||
Tree root = NULL; |
|||
int * found = NULL; |
|||
int * element = NULL; |
|||
|
|||
int search10 = 10; |
|||
int search64 = 64; |
|||
int search70 = 70; |
|||
int search80 = 80; |
|||
int search50 = 50; |
|||
|
|||
treeInsert(&root, newEle(40), insertCompare); |
|||
treeInsert(&root, newEle(50), insertCompare); |
|||
treeInsert(&root, newEle(60), insertCompare); |
|||
treeInsert(&root, newEle(70), insertCompare); |
|||
treeInsert(&root, newEle(80), insertCompare); |
|||
treeInsert(&root, newEle(45), insertCompare); |
|||
treeInsert(&root, newEle(75), insertCompare); |
|||
treeInsert(&root, newEle(85), insertCompare); |
|||
puts("traverse"); |
|||
treeWalk(root, printNode); |
|||
puts(""); |
|||
|
|||
element = newEle(70); |
|||
found = treeInsert(&root, element, insertCompare); |
|||
printf("insert %p(%d) got %p(%d)\n", element, *element, found, *found); |
|||
if (found != element) { |
|||
printf("remove duplicate"); |
|||
MEM_FREE(element); |
|||
} |
|||
puts("traverse"); |
|||
treeWalk(root, printNode); |
|||
puts(""); |
|||
|
|||
found = treeFind(root, &search10, insertCompare); |
|||
if (NULL == found) { |
|||
printf("can't find segmenet of minimum size: %d\n", 10); |
|||
} else { |
|||
printf("found %d\n", *found); |
|||
} |
|||
puts(""); |
|||
|
|||
found = treeFind(root, &search64, insertCompare); |
|||
if (NULL == found) { |
|||
printf("can't find segmenet of minimum size: %d\n", 64); |
|||
} else { |
|||
printf("found %d\n", *found); |
|||
} |
|||
puts(""); |
|||
|
|||
found = treeFind(root, &search70, insertCompare); |
|||
if (NULL == found) { |
|||
printf("can't find segmenet of minimum size: %d\n", 70); |
|||
} else { |
|||
printf("found %d\n", *found); |
|||
} |
|||
puts(""); |
|||
|
|||
found = treeDelete(&root, (void *)&search70, insertCompare); |
|||
printf("delete %p(%d) got %p(%d)\n", &search70, search70, found, *found); |
|||
MEM_FREE(found); |
|||
puts("traverse"); |
|||
treeWalk(root, printNode); |
|||
puts(""); |
|||
|
|||
found = treeInsert(&root, (void *)&search80, insertCompare); |
|||
printf("insert %p(%d) got %p(%d)\n", &search80, search80, found, *found); |
|||
found = treeInsert(&root, (void *)&search50, insertCompare); |
|||
printf("insert %p(%d) got %p(%d)\n", &search50, search50, found, *found); |
|||
found = treeInsert(&root, (void *)&search80, insertCompare); |
|||
printf("insert %p(%d) got %p(%d)\n", &search80, search80, found, *found); |
|||
|
|||
puts("traverse"); |
|||
treeWalk(root, printNode); |
|||
puts(""); |
|||
|
|||
found = treeDelete(&root, (void *)&search80, insertCompare); |
|||
printf("delete %p(%d) got %p(%d)\n", &search80, search80, found, *found); |
|||
MEM_FREE(found); |
|||
puts("traverse"); |
|||
treeWalk(root, printNode); |
|||
puts(""); |
|||
|
|||
found = treeDelete(&root, (void *)&search50, insertCompare); |
|||
printf("delete %p(%d) got %p(%d)\n", &search50, search50, found, *found); |
|||
MEM_FREE(found); |
|||
puts("traverse"); |
|||
treeWalk(root, printNode); |
|||
puts(""); |
|||
|
|||
found = treeDelete(&root, (void *)&search70, insertCompare); |
|||
printf("delete %p(%d) got %p(%d)\n", &search70, search70, found, found?*found:-1); |
|||
MEM_FREE(found); |
|||
puts("traverse"); |
|||
treeWalk(root, printNode); |
|||
puts(""); |
|||
|
|||
element = newEle(60); |
|||
found = treeDelete(&root, element, insertCompare); |
|||
printf("delete %p(%d) got %p(%d)\n", |
|||
element, |
|||
*element, |
|||
found, |
|||
found?*found:-1); |
|||
if (found != element) { |
|||
MEM_FREE(element); |
|||
} |
|||
MEM_FREE(found); |
|||
puts("traverse"); |
|||
treeWalk(root, printNode); |
|||
puts(""); |
|||
|
|||
treeDestroy(&root, freeNode); |
|||
memCleanup(); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
// vim: set et ts=4 sw=4: |
|||
@ -0,0 +1,9 @@ |
|||
ACLOCAL_AMFLAGS = -I m4 |
|||
|
|||
TREE = tree.c find.c insert.c inOrderSuccessor.c delete.c walk.c \
|
|||
rotateLeft.c rotateRight.c destroy.c |
|||
|
|||
noinst_LIBRARIES = libtree.a |
|||
|
|||
libtree_a_SOURCES = $(TREE) |
|||
libtree_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/ |
|||
@ -0,0 +1,249 @@ |
|||
/** |
|||
* \file |
|||
* |
|||
* \author Georg Hopp |
|||
* |
|||
* \copyright |
|||
* Copyright © 2012 Georg Hopp |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#include "tree.h" |
|||
|
|||
Tree inOrderSuccessor(Tree); |
|||
void treeRotateLeft(Tree *, Tree); |
|||
void treeRotateRight(Tree *, Tree); |
|||
|
|||
|
|||
void * |
|||
treeDelete(Tree * this, const void * search, TreeComp comp) |
|||
{ |
|||
Tree node = *this; |
|||
Tree del_node; |
|||
|
|||
void * data; |
|||
|
|||
/* |
|||
* first search for it and if its found return the data |
|||
* and we are done... |
|||
*/ |
|||
while (NULL != node) { |
|||
int comparison = comp(node->data, search); |
|||
|
|||
if (0 < comparison) { |
|||
node = TREE_LEFT(node); |
|||
continue; |
|||
} |
|||
|
|||
if (0 > comparison) { |
|||
node = TREE_RIGHT(node); |
|||
continue; |
|||
} |
|||
|
|||
if (0 == comparison) { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* nothing was found...return NULL to indicate this. |
|||
*/ |
|||
if (NULL == node) { |
|||
return NULL; |
|||
} |
|||
|
|||
/* |
|||
* we found an element, store its data pointer as we are |
|||
* up to delete it. |
|||
*/ |
|||
data = node->data; |
|||
|
|||
/* |
|||
* now remove the element. |
|||
*/ |
|||
|
|||
/* |
|||
* if we have two children replace data with the one from |
|||
* out inOrderSuccessor and remove the inOrderSuccessor. |
|||
*/ |
|||
if (NULL != TREE_LEFT(node) && NULL != TREE_RIGHT(node)) { |
|||
Tree successor = inOrderSuccessor(node); |
|||
|
|||
node->data = successor->data; |
|||
node = successor; |
|||
} |
|||
|
|||
{ |
|||
Tree child = TREE_CHILD(node); |
|||
|
|||
/* |
|||
* if we still have one child replace ourself with it. |
|||
*/ |
|||
TREE_REPLACE_NODE(this, node, child); |
|||
|
|||
/* |
|||
* and finally delete the node...and prepare ourselfs |
|||
* for rebalancing. |
|||
*/ |
|||
if (rbBlack == node->color) { |
|||
if (NULL != child && rbRed == child->color) { |
|||
child->color = rbBlack; |
|||
delete(node); |
|||
return data; |
|||
} else { |
|||
del_node = node; |
|||
if (NULL != child) { |
|||
node = child; |
|||
} else { |
|||
node->color = rbBlack; |
|||
node->left = NULL; |
|||
node->right = NULL; |
|||
} |
|||
} |
|||
} else { |
|||
delete(node); |
|||
return data; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* now comes rebalancing...note that if we came to this point |
|||
* the node is still not deleted. |
|||
* This is because I am not sure if it is needed during the |
|||
* rebalancing process...(this does not make much sense, but |
|||
* to be honest I don't know now.) |
|||
*/ |
|||
while(1) { |
|||
// case 1 |
|||
if (NULL == TREE_PARENT(node)) { |
|||
break; |
|||
} |
|||
|
|||
// case 2 |
|||
if (NULL != TREE_SIBLING(node) |
|||
&& rbRed == TREE_SIBLING(node)->color) { |
|||
|
|||
TREE_PARENT(node)->color = rbRed; |
|||
TREE_SIBLING(node)->color = rbBlack; |
|||
|
|||
if (NULL != TREE_PARENT(node)->right && |
|||
node != TREE_PARENT(node)->right) { |
|||
|
|||
//TREE_ROTATE_LEFT(this, TREE_PARENT(node)); |
|||
treeRotateLeft(this, TREE_PARENT(node)); |
|||
|
|||
} else { |
|||
|
|||
//TREE_ROTATE_RIGHT(this, TREE_PARENT(node)); |
|||
treeRotateRight(this, TREE_PARENT(node)); |
|||
|
|||
} |
|||
} |
|||
|
|||
// case 3 / 4 |
|||
if (NULL == TREE_SIBLING(node) |
|||
|| (rbBlack == TREE_SIBLING(node)->color |
|||
&& (NULL == TREE_SIBLING(node)->left |
|||
|| rbBlack == TREE_SIBLING(node)->left->color) |
|||
&& (NULL == TREE_SIBLING(node)->right |
|||
|| rbBlack == TREE_SIBLING(node)->right->color))) { |
|||
|
|||
if (NULL != TREE_SIBLING(node)) { |
|||
TREE_SIBLING(node)->color = rbRed; |
|||
} |
|||
|
|||
|
|||
/* |
|||
* this is the point where during the balancing our tree |
|||
* node can be finally deleted. |
|||
*/ |
|||
if (rbBlack == TREE_PARENT(node)->color) { |
|||
// case 3 |
|||
Tree parent = node->parent; |
|||
node = parent; |
|||
continue; |
|||
} else { |
|||
// case 4 |
|||
TREE_PARENT(node)->color = rbBlack; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// case 5 |
|||
if (NULL != TREE_SIBLING(node) |
|||
&& rbBlack == TREE_SIBLING(node)->color) { |
|||
|
|||
if (node == TREE_PARENT(node)->left |
|||
&& (NULL == TREE_SIBLING(node)->right |
|||
|| rbBlack == TREE_SIBLING(node)->right->color) |
|||
&& (NULL != TREE_SIBLING(node)->left |
|||
&& rbRed == TREE_SIBLING(node)->left->color)) { |
|||
|
|||
TREE_SIBLING(node)->color = rbRed; |
|||
TREE_SIBLING(node)->left->color = rbBlack; |
|||
|
|||
//TREE_ROTATE_RIGHT(this, TREE_SIBLING(node)); |
|||
treeRotateRight(this, TREE_SIBLING(node)); |
|||
|
|||
} else if (node == TREE_PARENT(node)->right |
|||
&& (NULL == TREE_SIBLING(node)->left |
|||
|| rbBlack == TREE_SIBLING(node)->left->color) |
|||
&& (NULL != TREE_SIBLING(node)->right |
|||
&& rbRed == TREE_SIBLING(node)->right->color)) { |
|||
|
|||
TREE_SIBLING(node)->color = rbRed; |
|||
TREE_SIBLING(node)->right->color = rbBlack; |
|||
|
|||
//TREE_ROTATE_LEFT(this, TREE_SIBLING(node)); |
|||
treeRotateLeft(this, TREE_SIBLING(node)); |
|||
} |
|||
} |
|||
|
|||
// case 6 |
|||
if (NULL != TREE_SIBLING(node)) { |
|||
TREE_SIBLING(node)->color = TREE_PARENT(node)->color; |
|||
} |
|||
|
|||
if (NULL != node && NULL != TREE_PARENT(node)) { |
|||
TREE_PARENT(node)->color = rbBlack; |
|||
|
|||
if (NULL != TREE_PARENT(node)->right |
|||
&& node != TREE_PARENT(node)->right) { |
|||
|
|||
if (NULL != TREE_SIBLING(node)->right) { |
|||
TREE_SIBLING(node)->right->color = rbBlack; |
|||
} |
|||
//TREE_ROTATE_LEFT(this, TREE_PARENT(node)); |
|||
treeRotateLeft(this, TREE_PARENT(node)); |
|||
} else { |
|||
if (NULL != TREE_SIBLING(node)->left) { |
|||
TREE_SIBLING(node)->left->color = rbBlack; |
|||
} |
|||
//TREE_ROTATE_RIGHT(this, TREE_PARENT(node)); |
|||
treeRotateRight(this, TREE_PARENT(node)); |
|||
} |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
delete(del_node); |
|||
/* |
|||
* not sure if deleting here is correct. |
|||
*/ |
|||
return data; |
|||
} |
|||
|
|||
// vim: set ts=4 sw=4: |
|||
@ -0,0 +1,85 @@ |
|||
/** |
|||
* \file |
|||
* |
|||
* \author Georg Hopp |
|||
* |
|||
* \copyright |
|||
* Copyright © 2012 Georg Hopp |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#include "tree.h" |
|||
|
|||
void |
|||
treeDestroy(Tree * this, TreeAction action) |
|||
{ |
|||
Tree previous = * this; |
|||
Tree node = * this; |
|||
int depth = 1; |
|||
|
|||
/* |
|||
* I think this has something like O(n+log(n)) on a ballanced |
|||
* tree because I have to traverse back the rightmost leaf to |
|||
* the root to get a break condition. |
|||
*/ |
|||
while (NULL != node) { |
|||
/* |
|||
* If we come from the right so nothing and go to our |
|||
* next parent. |
|||
*/ |
|||
if (((NULL == TREE_LEFT(node) || previous == TREE_LEFT(node)) |
|||
&& NULL == TREE_RIGHT(node)) |
|||
|| previous == TREE_RIGHT(node)) { |
|||
|
|||
Tree parent = TREE_PARENT(node); |
|||
|
|||
action(node->data, depth); |
|||
|
|||
previous = node; |
|||
delete(node); |
|||
node = parent; |
|||
depth--; |
|||
|
|||
continue; |
|||
} |
|||
|
|||
if ((NULL == TREE_LEFT(node) || previous == TREE_LEFT(node))) { |
|||
/* |
|||
* If there are no more elements to the left or we |
|||
* came from the left, process data. |
|||
*/ |
|||
previous = node; |
|||
|
|||
if (NULL != TREE_RIGHT(node)) { |
|||
node = TREE_RIGHT(node); |
|||
depth++; |
|||
} else { |
|||
node = TREE_PARENT(node); |
|||
depth--; |
|||
} |
|||
} else { |
|||
/* |
|||
* if there are more elements to the left go there. |
|||
*/ |
|||
previous = node; |
|||
node = TREE_LEFT(node); |
|||
depth++; |
|||
} |
|||
} |
|||
|
|||
*this = NULL; |
|||
} |
|||
|
|||
// vim: set ts=4 sw=4: |
|||
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue