Browse Source

Merge remote-tracking branch 'taskrambler/master'

release0.1.5
Georg Hopp 12 years ago
parent
commit
937593d7eb
  1. 4
      .gitignore
  2. 2
      Makefile.am.coverage
  3. 69
      TODO
  4. 13
      assets/html/foo.html
  5. 14
      assets/html/main.html
  6. 0
      assets/html/robots.txt
  7. 1
      assets/js/jquery.js
  8. 0
      assets/other/.keep-me
  9. 184
      config/mime.types
  10. 9
      configure.ac
  11. 26
      docs/idea_for_asset_access.md
  12. 184
      docs/mime.types
  13. 245
      docs/rbdelete.txt
  14. 235
      docs/rbinsert.txt
  15. 53
      docs/socket_states.txt
  16. 61
      include/asset.h
  17. 1
      include/cbuf.h
  18. 23
      include/cbufpool.h
  19. 3
      include/hash/hash.h
  20. 11
      include/http/message.h
  21. 9
      include/http/response.h
  22. 4
      include/http/worker.h
  23. 52
      include/http/writer.h
  24. 1
      include/socket.h
  25. 138
      include/tree.h
  26. 11
      include/utils/memory.h
  27. 31
      include/utils/mime_type.h
  28. 4
      m4/gcov.m4
  29. 13
      src/Makefile.am
  30. 6
      src/asset/Makefile.am
  31. 140
      src/asset/asset.c
  32. 94
      src/asset/pool.c
  33. 2
      src/auth/Makefile.am
  34. 8
      src/auth/credential.c
  35. 25
      src/auth/ldap.c
  36. 303
      src/binarytree.c
  37. 4
      src/cbuf/Makefile.am
  38. 4
      src/cbuf/cbuf.c
  39. 17
      src/cbuf/read.c
  40. 10
      src/cbufpool.c
  41. 2
      src/class/Makefile.am
  42. 12
      src/class/interface/i_class.c
  43. 54
      src/hash.c
  44. 2
      src/hash/Makefile.am
  45. 21
      src/hash/add.c
  46. 22
      src/hash/delete.c
  47. 10
      src/hash/each.c
  48. 21
      src/hash/get.c
  49. 9
      src/hash/hash.c
  50. 18
      src/hash/interface/hashable.c
  51. 8
      src/hash/value.c
  52. 3
      src/http/Makefile.am
  53. 12
      src/http/cookie.c
  54. 8
      src/http/header.c
  55. 24
      src/http/message.c
  56. 2
      src/http/parser.c
  57. 4
      src/http/parser/p_header.c
  58. 4
      src/http/parser/p_request_vars.c
  59. 54
      src/http/parser/parse.c
  60. 10
      src/http/request.c
  61. 4
      src/http/response.c
  62. 1
      src/http/response/304.c
  63. 1
      src/http/response/403.c
  64. 3
      src/http/response/404.c
  65. 65
      src/http/response/500.c
  66. 57
      src/http/response/asset.c
  67. 4
      src/http/response/login_form.c
  68. 4
      src/http/response/randval.c
  69. 4
      src/http/response/session.c
  70. 39
      src/http/worker.c
  71. 40
      src/http/worker/get_asset.c
  72. 101
      src/http/worker/process.c
  73. 13
      src/http/writer.c
  74. 141
      src/http/writer/write.c
  75. 2
      src/logger/Makefile.am
  76. 31
      src/logger/interface/i_logger.c
  77. 55
      src/mmapfiletest.c
  78. 54
      src/mmapfiletest2.c
  79. 2
      src/queue/Makefile.am
  80. 797
      src/rbtree.c
  81. 176
      src/rbtree2.c
  82. 2
      src/server/Makefile.am
  83. 55
      src/server/handle_accept.c
  84. 28
      src/server/poll.c
  85. 41
      src/server/read.c
  86. 90
      src/server/run.c
  87. 61
      src/server/server.c
  88. 24
      src/server/write.c
  89. 2
      src/session/Makefile.am
  90. 4
      src/session/session.c
  91. 4
      src/socket/Makefile.am
  92. 12
      src/socket/accept.c
  93. 32
      src/socket/nonblock.c
  94. 2
      src/stream/Makefile.am
  95. 26
      src/stream/read.c
  96. 25
      src/stream/write.c
  97. 14
      src/taskrambler.c
  98. 9
      src/tree/Makefile.am
  99. 249
      src/tree/delete.c
  100. 85
      src/tree/destroy.c

4
.gitignore

@ -27,4 +27,8 @@ coveragereport/
stamp-h1
src/taskrambler
/tests/*Test
/tests/*.log
/tests/*.trs
gmon.out
test-driver

2
Makefile.am.coverage

@ -21,7 +21,7 @@
generate-coverage-html:
@echo Collecting coverage data
$(LCOV) -d $(top_builddir) -c -o coverage.run --gcov-tool /usr/x86_64-pc-linux-gnu/gcc-bin/4.5.3/gcov
$(LCOV) -d $(top_builddir) -c -o coverage.run --gcov-tool /usr/bin/gcov
$(LCOV) -d $(top_builddir) -a ./coverage.base -a ./coverage.run -o coverage.info
LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --branch-coverage --show-details coverage.run

69
TODO

@ -6,3 +6,72 @@ VERY BIG TODO:
- handle errors after all system call...especially open, close, etc.
- IPV6 support
- There seem to be a problem in the server under heavy load. Some tests with ab show that at some point it does not accept any more connections. Nor does it seem to answer request. I guess that it might be difficult to find this....
=> might be done... maybe not completely, I still loose connections under very high load.
- using tsearch for my memory management was kind of a bad idea. tsearch uses malloc and free upon addition and remove of elements. Resulting again in uncontroolled heap usage. I guess I have to implement my own tree mechanism here, which does not involve further allocation.
- another bug: if there is trailing garbage on the header line
we got a segfault...
GET /images/waldschrat.jpg HTTP/1.1^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D^[[D
^[[D = backspace...
- the unexpected connection close seems to occur just on concurrency over
1000 paralell connections...maybe this gives a hint.
- after changing to non blocking sockets the server hangs after some requests.
have to check when this happens....
* it seems that it does not occur without keep-alive and concurrency
* also concurrent connection don't seem to make a problem.
* keep-alive alone again don't seem to be a problem...
!!!! if both are active something is screwed up.... !!!!
- at least some of the problems result in something that I think
is a bug in the glibc implementation of tdelete.
Under some circumstances tdelete does not search the whole tree.
Here comes some debug output of the code...
This shows a code fragment that tries to remove a no longer
needed asset from the asset_pool.
DEBUG: pool release asset --./assets/image/waldschrat.jpg--
Twalk over all elements in the tree:
DEBUG: ./assets/image/waldschrat.jpg(1910357807899405601) => 0xe378a8
DEBUG: ./assets/js/jquery.js(10519512221325982142) => 0x10d3638
Output of each call to tDelete comparison function:
(Notice that the searched asset hash was in the previous Twalk.
DEBUG: search asset hash: 1910357807899405601
DEBUG: found: 10519512221325982142, key: ./assets/js/jquery.js
DEBUG: !!!!! NOT FOUND !!!!!!!
DEBUG: find results in (nil)
Repeated 2 times if non was found...
DEBUG: search asset hash: 1910357807899405601
DEBUG: found: 10519512221325982142, key: ./assets/js/jquery.js
DEBUG: !!!!! NOT FOUND !!!!!!!
DEBUG: find results in (nil)
DEBUG: search asset hash: 1910357807899405601
DEBUG: found: 10519512221325982142, key: ./assets/js/jquery.js
DEBUG: !!!!! NOT FOUND !!!!!!!
DEBUG: find results in (nil)
===
Again a twalk over all elements...
DEBUG: ./assets/image/waldschrat.jpg(1910357807899405601) => 0xe378a8
DEBUG: ./assets/js/jquery.js(10519512221325982142) => 0x10d3638
Final statement...
DEBUG delete 0xe378a8, parent was (nil)
So, it looks like I should build my own tree implementation...
well in fact it seems that I have to!

13
assets/html/foo.html

@ -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: -->

14
assets/html/main.html

@ -4,11 +4,11 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>My own little Web-App</title>
<link rel="stylesheet" type="text/css" href="/assets/style/common">
<script type="text/javascript" src="/assets/js/jquery"></script>
<script type="text/javascript" src="/assets/js/serverval"></script>
<script type="text/javascript" src="/assets/js/session"></script>
<script type="text/javascript" src="/assets/js/init"></script>
<link rel="stylesheet" type="text/css" href="/style/common.css">
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/serverval.js"></script>
<script type="text/javascript" src="/js/session.js"></script>
<script type="text/javascript" src="/js/init.js"></script>
</head>
<body>
<ul id="menu">
@ -30,7 +30,7 @@
</form>
</div>
<div id="randval" class="hide">
<span class=\"small">
<span class="small">
Value created at: <br />
<span></span><br>
Next value in: <span></span><br />
@ -40,7 +40,7 @@
<div id="main">
<h1>Testpage</h1>
Welcome<span></span>!!!<br />
<img src="/image/me" />
<a href="/foo.html"><img src="/image/waldschrat.jpg" /></a>
</div>
<hr />
<div id="msg"></div>

0
assets/html/robots.txt

1
assets/js/jquery.js

@ -0,0 +1 @@
jquery-1.7.1.min.js

0
assets/other/.keep-me

184
config/mime.types

@ -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

9
configure.ac

@ -2,12 +2,12 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.68])
AC_INIT([taskrambler], [0.0.1], [Georg Hopp <georg@steffers.org>])
AC_INIT([taskrambler], [0.1.2], [Georg Hopp <georg@steffers.org>])
LT_INIT
AM_INIT_AUTOMAKE
#AM_INIT_AUTOMAKE([subdir-objects])
AM_SILENT_RULES([yes])
AC_COPYRIGHT([Copyright © 2012 Georg Hopp])
AC_COPYRIGHT([Copyright © 2013 Georg Hopp])
AC_REVISION([$Revision: 0.02 $])
AC_CONFIG_SRCDIR([src/taskrambler.c])
AC_CONFIG_HEADERS([config.h])
@ -19,6 +19,9 @@ AC_SUBST(COVERAGE_CFLAGS)
AC_SUBST(COVERAGE_CXXFLAGS)
AC_SUBST(COVERAGE_LDFLAGS)
PWD=$(/bin/pwd)
AC_SUBST(PWD)
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
@ -48,6 +51,7 @@ AC_CHECK_FUNCS([memset])
AC_CONFIG_FILES([Makefile
src/Makefile
src/asset/Makefile
src/auth/Makefile
src/cbuf/Makefile
src/class/Makefile
@ -59,5 +63,6 @@ AC_CONFIG_FILES([Makefile
src/session/Makefile
src/socket/Makefile
src/stream/Makefile
src/tree/Makefile
tests/Makefile])
AC_OUTPUT

26
docs/idea_for_asset_access.md

@ -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.

184
docs/mime.types

@ -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

245
docs/rbdelete.txt

@ -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:

235
docs/rbinsert.txt

@ -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:

53
docs/socket_states.txt

@ -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

61
include/asset.h

@ -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:

1
include/cbuf.h

@ -38,6 +38,7 @@
#include "stream.h"
#include "commons.h"
#include "utils/memory.h"
#define ECBUFOVFL 100

23
include/cbufpool.h

@ -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.
*/

3
include/hash/hash.h

@ -26,10 +26,11 @@
#include <sys/types.h>
#include "class.h"
#include "tree.h"
CLASS(Hash) {
void * root;
Tree root;
};
void * hashAdd(Hash, void *);

11
include/http/message.h

@ -27,21 +27,16 @@
#include "class.h"
#include "hash.h"
#include "stream.h"
typedef enum e_HttpMessageType {
HTTP_MESSAGE_BUFFERED=0,
HTTP_MESSAGE_PIPED
} HttpMessageType;
#include "asset.h"
CLASS(HttpMessage) {
char * version;
Hash header;
HttpMessageType type;
Stream handle;
Asset asset;
char * body;
int nbody;
int dbody;
};

9
include/http/response.h

@ -30,6 +30,7 @@
#include "class.h"
#include "http/message.h"
#include "session.h"
#include "asset.h"
CLASS(HttpResponse) {
@ -43,16 +44,14 @@ HttpResponse httpResponse304(
const char *, size_t,
const char *, size_t,
const char *, size_t);
HttpResponse httpResponse404();
HttpResponse httpResponse403();
HttpResponse httpResponse404();
HttpResponse httpResponse500();
HttpResponse httpResponseMe();
HttpResponse httpResponseLoginForm();
HttpResponse httpResponseRandval(time_t, int);
HttpResponse httpResponseSession(Session);
HttpResponse httpResponseAsset(
const char *,
const char *, size_t,
const char *, size_t);
HttpResponse httpResponseAsset(const char *, size_t);
#endif // __HTTP_RESPONSE_H__

4
include/http/worker.h

@ -28,6 +28,7 @@
#include <time.h>
#include "class.h"
#include "hash.h"
#include "http/parser.h"
#include "http/writer.h"
#include "cbuf.h"
@ -46,7 +47,8 @@ CLASS(HttpWorker) {
struct randval * val;
Cbuf pbuf;
Cbuf wbuf;
Hash asset_pool;
HttpParser parser;
HttpWriter writer;

52
include/http/writer.h

@ -29,14 +29,48 @@
#include "class.h"
#include "http/message.h"
#include "queue.h"
#include "cbuf.h"
#include "stream.h"
#include "commons.h"
#define WRITER_MAX_BUF 131072
/*
* the buffer that will be written back to an http client.
* If we have open 1024 paralell connection this will result
* in a memory usage of 128MB. Right now, we don't allow more
* than this amount of paralell connections.
*
* This one and the parser buffer are the hugest memory pools
* we need. The parser buffer is of the same size.
*
* Right now only the ringbuffer is reused for each connection
* resulting in some memory movement between some temporary
* space and the circular buffer.
*
* This behavioru should be kept in place for low memory machines
* running this code.
*
* Anyway, I will build a version which uses two ringbuffers for
* each connection, Resulting in a 256KB memory used for each
* connection. Which in turn means 256MB for 1024 paralell connections.
*
* And as I will also implement a cbuf pool, this memory will not be
* freed before application end.
*/
/*
* This is the multiplier for the size of the initial write buffer.
* It is used to store the
* string representation of the message, as well as the first part of
* the body if the headers exceed the size a multiple of this will
* be reserved...very unlikely, but not impossible.
* If no the whole body fits within this buffer only part of it will
* be copied in there. The rest will be send in following send calls.
*/
#define WRITER_BUF_CHUNK 1024 * 10 // our default buffer chunk for
// headers is 10k. This will result
// in at least 20m for 2000 concurrent
// connections.
typedef enum e_HttpWriterState {
HTTP_WRITER_GET=0,
@ -45,15 +79,15 @@ typedef enum e_HttpWriterState {
} HttpWriterState;
CLASS(HttpWriter) {
Cbuf buffer;
Bool ourLock;
char * buffer;
Queue queue;
HttpMessage current;
Queue queue;
HttpMessage current;
size_t nheader;
size_t nbody;
size_t written;
size_t nbuffer; // size of buffer
size_t nheader; // size headers in buf
size_t nbody; // sizeof body in buffer
size_t written; // already written bytes
HttpWriterState state;
};

1
include/socket.h

@ -40,6 +40,7 @@ CLASS(Sock) {
void socketConnect(Sock this, const char * addr, char (*)[16]);
void socketListen(Sock this, int backlog);
Sock socketAccept(Sock this, char (*remoteAddr)[16]);
void socketNonblock(Sock this);
#endif // __SOCKET_H__

138
include/tree.h

@ -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:

11
include/utils/memory.h

@ -25,7 +25,16 @@
#define CSTRA(val) val, sizeof(val)-1 //!< Const STRing Argument
#define FREE(val) (ffree((void**)&(val)))
#define FREE(val) (ffree((void**)&(val)))
#define MEM_FREE(seg) (memFree((void **)&(seg)))
#include <sys/types.h>
void * memMalloc(size_t);
void * memCalloc(size_t, size_t);
void memFree(void **);
size_t memGetSize(void *);
void memCleanup();
void ffree(void **);

31
include/utils/mime_type.h

@ -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:

4
m4/gcov.m4

@ -14,7 +14,7 @@ AC_DEFUN([AC_TDD_GCOV],
[use_gcov=$enableval], [use_gcov=no])
AM_CONDITIONAL(HAVE_GCOV, test "x$use_gcov" = "xyes")
# if test "x$use_gcov" = "xyes"; then
if test "x$use_gcov" = "xyes"; then
# we need gcc:
if test "$GCC" != "yes"; then
AC_MSG_ERROR([GCC is required for --enable-gcov])
@ -72,6 +72,6 @@ AC_DEFUN([AC_TDD_GCOV],
COVERAGE_CXXFLAGS="-O0 -fprofile-arcs -ftest-coverage"
COVERAGE_LDFLAGS="-lgcov"
#fi
fi
]) # AC_TDD_GCOV

13
src/Makefile.am

@ -2,31 +2,36 @@ ACLOCAL_AMFLAGS = -I m4
IFACE = interface/subject.c \
interface/observer.c
UTILS = utils/hash.c \
utils/memory.c \
utils/http.c \
utils/daemonize.c \
utils/signalHandling.c
utils/signalHandling.c \
utils/mime_type.c
LIBS = ./http/libhttp.a \
./auth/libauth.a \
./cbuf/libcbuf.a \
./class/libclass.a \
./asset/libasset.a \
./hash/libhash.a \
./queue/libqueue.a \
./logger/liblogger.a \
./server/libserver.a \
./session/libsession.a \
./socket/libsocket.a \
./stream/libstream.a
./stream/libstream.a \
./tree/libtree.a
AM_CFLAGS = -Wall -I ../include/
bin_PROGRAMS = taskrambler
taskrambler_SOURCES = taskrambler.c $(IFACE) $(UTILS)
taskrambler_CFLAGS = -Wall -I ../include/# $(COVERAGE_CFLAGS)
taskrambler_CFLAGS = $(CFLAGS) -Wall -DPWD=\"$(PWD)\" -I ../include/# $(COVERAGE_CFLAGS)
taskrambler_LDADD = $(LIBS) -lrt -lssl -lldap
#taskrambler_LDFLAGS = $(COVERAGE_LDFLAGS)
SUBDIRS = auth cbuf class hash queue http logger server session socket stream
SUBDIRS = asset auth cbuf class hash queue http \
logger server session socket stream tree

6
src/asset/Makefile.am

@ -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/

140
src/asset/asset.c

@ -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:

94
src/asset/pool.c

@ -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:

2
src/auth/Makefile.am

@ -3,4 +3,4 @@ ACLOCAL_AMFLAGS = -I m4
noinst_LIBRARIES = libauth.a
libauth_a_SOURCES = interface/auth.c credential.c ldap.c
libauth_a_CFLAGS = -Wall -I ../../include/
libauth_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

8
src/auth/credential.c

@ -48,11 +48,11 @@ credentialCtor(void * _this, va_list * params)
pass = va_arg(* params, char*);
CRED_PWD(this).npass = va_arg(* params, size_t);
CRED_PWD(this).user = malloc(CRED_PWD(this).nuser + 1);
CRED_PWD(this).user = memMalloc(CRED_PWD(this).nuser + 1);
CRED_PWD(this).user[CRED_PWD(this).nuser] = 0;
memcpy(CRED_PWD(this).user, user, CRED_PWD(this).nuser);
CRED_PWD(this).pass = malloc(CRED_PWD(this).npass + 1);
CRED_PWD(this).pass = memMalloc(CRED_PWD(this).npass + 1);
CRED_PWD(this).pass[CRED_PWD(this).npass] = 0;
memcpy(CRED_PWD(this).pass, pass, CRED_PWD(this).npass);
}
@ -73,8 +73,8 @@ credentialDtor(void * _this)
switch(this->type) {
case CRED_PASSWORD:
FREE(CRED_PWD(this).user);
FREE(CRED_PWD(this).pass);
MEM_FREE(CRED_PWD(this).user);
MEM_FREE(CRED_PWD(this).pass);
break;
}
}

25
src/auth/ldap.c

@ -42,7 +42,7 @@ authLdapCtor(void * _this, va_list * params)
char * url = va_arg(*params, char*);
char * base_dn;
this->url = malloc(strlen(url) + 1);
this->url = memMalloc(strlen(url) + 1);
strcpy(this->url, url);
this->version = 3;
@ -50,7 +50,7 @@ authLdapCtor(void * _this, va_list * params)
base_dn = va_arg(* params, char *);
this->nbase_dn = va_arg(* params, size_t);
this->base_dn = malloc(this->nbase_dn + 1);
this->base_dn = memMalloc(this->nbase_dn + 1);
this->base_dn[this->nbase_dn] = 0;
memcpy(this->base_dn, base_dn, this->nbase_dn);
@ -63,8 +63,8 @@ authLdapDtor(void * _this)
{
AuthLdap this = _this;
FREE(this->base_dn);
FREE(this->url);
MEM_FREE(this->base_dn);
MEM_FREE(this->url);
}
static
@ -76,6 +76,9 @@ authLdapAuthenticate(void * _this, Credential cred)
char * who_ptr = who;
int ldap_err;
struct berval ldap_cred;
struct berval * ldap_servcred;
if (CRED_PASSWORD != cred->type) {
return FALSE;
}
@ -91,9 +94,19 @@ authLdapAuthenticate(void * _this, Credential cred)
memcpy(who_ptr, this->base_dn, this->nbase_dn);
who_ptr[this->nbase_dn] = 0;
ldap_err = ldap_simple_bind_s(this->ldap, who, CRED_PWD(cred).pass);
ldap_cred.bv_val = CRED_PWD(cred).pass;
ldap_cred.bv_len = CRED_PWD(cred).npass;
ldap_err = ldap_sasl_bind_s(
this->ldap,
who,
LDAP_SASL_SIMPLE,
&ldap_cred,
NULL,
NULL,
&ldap_servcred);
if (0 == ldap_err) {
ldap_unbind_s(this->ldap);
ldap_unbind_ext_s(this->ldap, NULL, NULL);
//! \todo here we need to get and return the user id
return TRUE;
}

303
src/binarytree.c

@ -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:

4
src/cbuf/Makefile.am

@ -1,6 +1,6 @@
ACLOCAL_AMFLAGS = -I m4
CB = cbuf.c read.c write.c \
CB = cbuf.c read.c \
get_line.c set_data.c get_data.c \
addr_index.c get_free.c get_read.c get_write.c \
inc_read.c inc_write.c is_empty.c memchr.c \
@ -10,4 +10,4 @@ CB = cbuf.c read.c write.c \
noinst_LIBRARIES = libcbuf.a
libcbuf_a_SOURCES = $(CB)
libcbuf_a_CFLAGS = -Wall -I ../../include/
libcbuf_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

4
src/cbuf/cbuf.c

@ -53,7 +53,7 @@ cbufCtor(void * _this, va_list * params)
int shm;
char * data;
this->shm_name = malloc(strlen(shm_name) + 7 + 2);
this->shm_name = memMalloc(strlen(shm_name) + 7 + 2);
sprintf(this->shm_name, "/%06d_%s", getpid(), shm_name);
/**
@ -110,7 +110,7 @@ cbufDtor(void * _this)
{
Cbuf this = _this;
FREE(this->shm_name);
MEM_FREE(this->shm_name);
if (NULL != this->data && MAP_FAILED != this->data) {
munmap(this->data, this->bsize << 1);

17
src/cbuf/read.c

@ -31,8 +31,8 @@
ssize_t
cbufRead(Cbuf this, Stream st)
{
ssize_t rrsize = 0;
size_t rsize = cbufGetFree(this);
size_t rsize = cbufGetFree(this);
ssize_t rrsize;
if (0 == rsize) {
errno = ECBUFOVFL;
@ -41,17 +41,8 @@ cbufRead(Cbuf this, Stream st)
rrsize = streamRead(st, cbufGetWrite(this), rsize);
switch (rrsize) {
case 0:
rrsize = -2;
// DROP THROUGH
case -1:
break;
default:
cbufIncWrite(this, rrsize);
break;
if (0 < rrsize) {
cbufIncWrite(this, rrsize);
}
return rrsize;

10
src/cbufpool.c

@ -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.
*/

2
src/class/Makefile.am

@ -3,4 +3,4 @@ ACLOCAL_AMFLAGS = -I m4
noinst_LIBRARIES = libclass.a
libclass_a_SOURCES = interface.c interface/i_class.c
libclass_a_CFLAGS = -Wall -I ../../include/
libclass_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

12
src/class/interface/i_class.c

@ -27,6 +27,9 @@
#include "class/class.h"
#include "class/interface/class.h"
#include "utils/memory.h"
const
struct interface i_Class = {
"class",
@ -36,7 +39,7 @@ struct interface i_Class = {
void *
classNew(class_ptr class, ...)
{
void * object = calloc(1, class->object_size + sizeof(void*));
void * object = memCalloc(1, class->object_size + sizeof(void*));
va_list params;
int ret;
@ -58,9 +61,12 @@ void
classDelete(void ** object)
{
if (NULL != *object) {
void * mem;
CALL(*object, Class, dtor);
free(*object - sizeof(void*));
mem = *object - sizeof(void*);
MEM_FREE(mem);
*object = NULL;
}
}
@ -69,7 +75,7 @@ void *
classClone(void * _object)
{
class_ptr class = GET_CLASS(_object);
void * object = calloc(1, class->object_size + sizeof(void*));
void * object = memCalloc(1, class->object_size + sizeof(void*));
* (class_ptr *)object = class;
object += sizeof(void*);

54
src/hash.c

@ -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:

2
src/hash/Makefile.am

@ -6,4 +6,4 @@ HASH = hash.c add.c get.c delete.c each.c value.c \
noinst_LIBRARIES = libhash.a
libhash_a_SOURCES = $(HASH)
libhash_a_CFLAGS = -Wall -I ../../include/
libhash_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

21
src/hash/add.c

@ -30,24 +30,35 @@ inline
int
hashAddComp(const void * a, const void * b)
{
return hashableGetHash((void*)b) - hashableGetHash((void*)a);
unsigned long hash_a = hashableGetHash((void*)a);
unsigned long hash_b = hashableGetHash((void*)b);
if (hash_a < hash_b) {
return -1;
}
if (hash_a > hash_b) {
return 1;
}
return 0;
}
void *
hashAdd(Hash this, void * operand)
{
void * found = tsearch(operand, &(this->root), hashAddComp);
void * found = treeInsert(&this->root, operand, hashAddComp);
if (NULL == found) {
return NULL;
}
if (operand != *(void**)found) {
hashableHandleDouble(*(void**)found, operand);
if (operand != found) {
hashableHandleDouble(found, operand);
delete(operand);
}
return *(void**)found;
return found;
}
// vim: set ts=4 sw=4:

22
src/hash/delete.c

@ -20,9 +20,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <search.h>
#include <sys/types.h>
#include "asset.h"
#include "hash.h"
#include "utils/hash.h"
@ -31,16 +31,28 @@ inline
int
hashDeleteComp(const void * a, const void * b)
{
return hashableGetHash((void*)b) - *(const unsigned long*)a;
unsigned long hash_a = hashableGetHash((void*)a);
if (hash_a < *(const unsigned long*)b) {
return -1;
}
if (hash_a > *(const unsigned long*)b) {
return 1;
}
return 0;
}
void *
hashDelete(Hash this, const char * search, size_t nsearch)
{
unsigned long hash = sdbm((const unsigned char *)search, nsearch);
void * found = tfind(&hash, &(this->root), hashDeleteComp);
unsigned long hash = sdbm((const unsigned char *)search, nsearch);
void * found = NULL;
found = treeDelete(&(this->root), &hash, hashDeleteComp);
return (NULL != found)? *(void**)found : NULL;
return found;
}
// vim: set ts=4 sw=4:

10
src/hash/each.c

@ -24,16 +24,14 @@
#include "hash.h"
static void (*cb)(void*);
static void (*cb)(const void*);
static
inline
void
walk(const void * node, const VISIT which, const int depth)
walk(const void * node, const int depth)
{
if (endorder == which || leaf == which) {
cb(*(void**)node);
}
cb(node);
}
void
@ -41,7 +39,7 @@ hashEach(Hash this, void (*callback)(const void*))
{
cb = callback;
twalk(this->root, walk);
treeWalk(this->root, walk);
}
// vim: set ts=4 sw=4:

21
src/hash/get.c

@ -20,10 +20,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <search.h>
#include <sys/types.h>
#include "hash.h"
#include "tree.h"
#include "utils/hash.h"
static
@ -31,16 +34,26 @@ inline
int
hashGetComp(const void * a, const void * b)
{
return hashableGetHash((void*)b) - *(const unsigned long*)a;
unsigned long hash_a = hashableGetHash((void*)a);
if (hash_a < *(const unsigned long*)b) {
return -1;
}
if (hash_a > *(const unsigned long*)b) {
return 1;
}
return 0;
}
void *
hashGet(Hash this, const char * search, size_t nsearch)
{
unsigned long hash = sdbm((const unsigned char *)search, nsearch);
void * found = tfind(&hash, &(this->root), hashGetComp);
unsigned long hash = sdbm((const unsigned char *)search, nsearch);
void * found = treeFind(this->root, &hash, hashGetComp);
return (NULL != found)? *(void**)found : NULL;
return found;
}
// vim: set ts=4 sw=4:

9
src/hash/hash.c

@ -38,7 +38,7 @@ hashCtor(void * _this, va_list * params)
static
inline
void
tDelete(void * node)
tDelete(const void * node, const int depth)
{
delete(node);
}
@ -49,12 +49,7 @@ hashDtor(void * _this)
{
Hash this = _this;
/**
* this is a GNU extension...anyway on most non
* GNUish systems i would not use tsearch anyway
* as the trees will be unbalanced.
*/
tdestroy(this->root, tDelete);
treeDestroy(&this->root, tDelete);
}
INIT_IFACE(Class, hashCtor, hashDtor, NULL);

18
src/hash/interface/hashable.c

@ -37,7 +37,23 @@ hashableGetHash(void * hashable)
{
unsigned long ret;
RETCALL(hashable, Hashable, getHash, ret);
//RETCALL(hashable, Hashable, getHash, ret);
do {
struct i_Hashable * iface;
//_CALL(GET_CLASS(hashable), Hashable, getHash);
do {
class_ptr class = GET_CLASS(hashable);
iface = (struct i_Hashable *)IFACE_GET(class, &i_Hashable);
while ((NULL == iface || NULL == iface->getHash) && HAS_PARENT(class)) {
class = class->parent;
iface = (struct i_Hashable *)IFACE_GET(class, &i_Hashable);
}
assert(NULL != iface->getHash);
} while(0);
ret = iface->getHash(hashable);
} while(0);
return ret;
}

8
src/hash/value.c

@ -45,14 +45,14 @@ hashValueCtor(void * _this, va_list * params)
value = va_arg(* params, void*);
this->nvalue = va_arg(* params, size_t);
this->key = malloc(this->nkey + 1);
this->key = memMalloc(this->nkey + 1);
this->key[this->nkey] = 0;
memcpy(this->key, key, this->nkey);
this->hash = sdbm((unsigned char *)this->key, this->nkey);
if (NULL != value) {
this->value = malloc(this->nvalue + 1);
this->value = memMalloc(this->nvalue + 1);
((char*)this->value)[this->nvalue] = 0;
memcpy(this->value, value, this->nvalue);
}
@ -66,8 +66,8 @@ hashValueDtor(void * _this)
{
HashValue this = _this;
FREE(this->key);
FREE(this->value);
MEM_FREE(this->key);
MEM_FREE(this->value);
}
static

3
src/http/Makefile.am

@ -12,6 +12,7 @@ RESP = response.c \
response/304.c \
response/404.c \
response/403.c \
response/500.c \
response/login_form.c \
response/asset.c \
response/randval.c \
@ -37,4 +38,4 @@ noinst_LIBRARIES = libhttp.a
libhttp_a_SOURCES = $(MSG) $(REQ) $(RESP) $(PARSER) $(WRITER) \
$(WORKER) $(HEADER) interface/i_http_intro.c
libhttp_a_CFLAGS = -Wall -I ../../include/
libhttp_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

12
src/http/cookie.c

@ -46,11 +46,11 @@ httpCookieCtor(void * _this, va_list * params)
value = va_arg(* params, char*);
this->nvalue = va_arg(* params, size_t);
this->key = malloc(this->nkey + 1);
this->key = memMalloc(this->nkey + 1);
this->key[this->nkey] = 0;
memcpy(this->key, key, this->nkey);
this->value = malloc(this->nvalue + 1);
this->value = memMalloc(this->nvalue + 1);
this->value[this->nvalue] = 0;
memcpy(this->value, value, this->nvalue);
@ -65,10 +65,10 @@ httpCookieDtor(void * _this, va_list * params)
{
HttpCookie this = _this;
FREE(this->key);
FREE(this->value);
FREE(this->domain);
FREE(this->path);
MEM_FREE(this->key);
MEM_FREE(this->value);
MEM_FREE(this->domain);
MEM_FREE(this->path);
}
static

8
src/http/header.c

@ -43,13 +43,13 @@ httpHeaderCtor(void * _this, va_list * params) {
value = va_arg(* params, char *);
this->nvalue[0] = va_arg(* params, size_t);
this->name = malloc(this->nname + 1);
this->name = memMalloc(this->nname + 1);
this->name[this->nname] = 0;
memcpy(this->name, name, this->nname);
this->hash = sdbm((unsigned char *)name, this->nname);
(this->value)[0] = malloc((this->nvalue)[0] + 1);
(this->value)[0] = memMalloc((this->nvalue)[0] + 1);
(this->value)[0][(this->nvalue)[0]] = 0;
memcpy((this->value)[0], value, (this->nvalue)[0]);
this->cvalue = 1;
@ -65,10 +65,10 @@ httpHeaderDtor(void * _this)
HttpHeader this = _this;
size_t i;
FREE(this->name);
MEM_FREE(this->name);
for (i=0; i<this->cvalue; i++) {
FREE(this->value[i]);
MEM_FREE(this->value[i]);
}
}

24
src/http/message.c

@ -43,7 +43,7 @@ httpMessageCtor(void * _this, va_list * params)
HttpMessage this = _this;
char * version = va_arg(* params, char *);
this->version = calloc(1, strlen(version)+1);
this->version = memCalloc(1, strlen(version)+1);
strcpy(this->version, version);
this->header = new(Hash);
@ -59,22 +59,14 @@ httpMessageDtor(void * _this)
delete(this->header);
FREE(this->version);
MEM_FREE(this->version);
switch (this->type) {
case HTTP_MESSAGE_BUFFERED:
FREE(this->body);
break;
case HTTP_MESSAGE_PIPED:
if (2 < (this->handle->handle).fd) {
close((this->handle->handle).fd);
}
delete(this->handle);
break;
default:
break;
if (NULL == this->asset) {
MEM_FREE(this->body);
} else {
assetPoolRelease(this->asset);
this->asset = NULL;
this->body = NULL;
}
}

2
src/http/parser.c

@ -64,7 +64,7 @@ httpParserDtor(void * _this)
if (TRUE == this->ourLock)
cbufRelease(this->buffer);
FREE(this->incomplete);
MEM_FREE(this->incomplete);
delete(this->current);
}

4
src/http/parser/p_header.c

@ -31,6 +31,8 @@
#include "http/request.h"
#include "hash.h"
#include "utils/memory.h"
void
httpParserHeader(
HttpParser this,
@ -55,7 +57,7 @@ httpParserHeader(
if (0 == strncasecmp("content-length", name, nname-1)) {
current->nbody = strtoul(value, NULL, 10);
if (0 < this->current->nbody) {
current->body = malloc(current->nbody);
current->body = memMalloc(current->nbody);
}
current->dbody = 0;
}

4
src/http/parser/p_request_vars.c

@ -29,6 +29,8 @@
#include "hash.h"
#include "class.h"
#include "utils/memory.h"
void
httpParserRequestVars(HttpParser this)
{
@ -39,7 +41,7 @@ httpParserRequestVars(HttpParser this)
delim = request->uri + strlen(request->uri);
}
request->path = malloc(delim - request->uri + 1);
request->path = memMalloc(delim - request->uri + 1);
request->path[delim - request->uri] = 0;
memcpy(request->path, request->uri, delim - request->uri);

54
src/http/parser/parse.c

@ -57,11 +57,12 @@ httpParserParse(void * _this, Stream st)
if (NULL != this->incomplete) {
cbufSetData(this->buffer, this->incomplete, this->isize);
free(this->incomplete);
this->incomplete = NULL;
MEM_FREE(this->incomplete);
}
if (0 > (read = cbufRead(this->buffer, st))) {
cbufRelease(this->buffer);
this->ourLock = FALSE;
return read;
}
@ -76,15 +77,14 @@ httpParserParse(void * _this, Stream st)
cbufRelease(this->buffer);
this->ourLock = FALSE;
cont = 0;
break;
}
break;
case HTTP_MESSAGE_START:
if (NULL == (line = cbufGetLine(this->buffer, &line_end))) {
if (! cbufIsEmpty(this->buffer)) {
this->isize = this->buffer->bused;
this->incomplete = malloc(this->isize);
this->isize = this->buffer->bused;
this->incomplete = memMalloc(this->isize);
memcpy(this->incomplete,
cbufGetData(this->buffer, this->isize),
this->isize);
@ -99,18 +99,17 @@ httpParserParse(void * _this, Stream st)
if (NULL == this->current) {
cbufRelease(this->buffer);
this->ourLock = FALSE;
return -1;
return -2; // a server error occured can't process...
}
httpParserRequestVars(this);
this->state = HTTP_MESSAGE_INTRO_DONE;
break;
case HTTP_MESSAGE_INTRO_DONE:
if (NULL == (line = cbufGetLine(this->buffer, &line_end))) {
if (! cbufIsEmpty(this->buffer)) {
this->isize = this->buffer->bused;
this->incomplete = malloc(this->isize);
this->isize = this->buffer->bused;
this->incomplete = memMalloc(this->isize);
memcpy(this->incomplete,
cbufGetData(this->buffer, this->isize),
this->isize);
@ -121,35 +120,34 @@ httpParserParse(void * _this, Stream st)
break;
}
if (0 == strlen(line)) {
this->state = HTTP_MESSAGE_HEADERS_DONE;
if (0 != strlen(line)) {
httpParserHeader(this, line, line_end);
break;
}
httpParserHeader(this, line, line_end);
break;
this->state = HTTP_MESSAGE_HEADERS_DONE;
case HTTP_MESSAGE_HEADERS_DONE:
if (this->current->dbody == this->current->nbody) {
this->state = HTTP_MESSAGE_DONE;
break;
}
} else {
if (cbufIsEmpty(this->buffer)) {
cbufRelease(this->buffer);
this->ourLock = FALSE;
cont = 0;
break;
}
cbufIncRead(
this->buffer,
httpParserBody(
this,
cbufGetRead(this->buffer),
this->buffer->bused));
if (cbufIsEmpty(this->buffer)) {
cbufRelease(this->buffer);
this->ourLock = FALSE;
cont = 0;
break;
}
cbufIncRead(
this->buffer,
httpParserBody(
this,
cbufGetRead(this->buffer),
this->buffer->bused));
break;
case HTTP_MESSAGE_DONE:
{
HttpHeader enc = hashGet(

10
src/http/request.c

@ -48,11 +48,11 @@ httpRequestCtor(void * _this, va_list * params)
PARENTCALL(_this, Class, ctor, params);
this->method = malloc(mlen + 1);
this->method = memMalloc(mlen + 1);
this->method[mlen] = 0;
memcpy(this->method, method, mlen);
this->uri = malloc(ulen + 1);
this->uri = memMalloc(ulen + 1);
this->uri[ulen] = 0;
memcpy(this->uri, uri, ulen);
@ -73,9 +73,9 @@ httpRequestDtor(void * _this)
delete(this->post);
delete(this->cookies);
FREE(this->uri);
FREE(this->method);
FREE(this->path);
MEM_FREE(this->uri);
MEM_FREE(this->method);
MEM_FREE(this->path);
PARENTCALL(_this, Class, dtor);
}

4
src/http/response.c

@ -45,7 +45,7 @@ httpResponseCtor(void * _this, va_list * params)
this->status = va_arg(* params, unsigned int);
reason = va_arg(* params, char *);
this->reason = calloc(1, strlen(reason)+1);
this->reason = memCalloc(1, strlen(reason)+1);
strcpy(this->reason, reason);
return 0;
@ -57,7 +57,7 @@ httpResponseDtor(void * _this)
{
HttpResponse this = _this;
FREE(this->reason);
MEM_FREE(this->reason);
PARENTCALL(_this, Class, dtor);
}

1
src/http/response/304.c

@ -43,7 +43,6 @@ httpResponse304(
response = new(HttpResponse, "HTTP/1.1", 304, "Not Modified");
message = (HttpMessage)response;
message->type = HTTP_MESSAGE_BUFFERED;
message->nbody = 0;
message->body = NULL;

1
src/http/response/403.c

@ -41,7 +41,6 @@ httpResponse403()
response = new(HttpResponse, "HTTP/1.1", 403, "Forbidden");
message = (HttpMessage)response;
message->type = HTTP_MESSAGE_BUFFERED;
message->nbody = 0;
message->body = NULL;

3
src/http/response/404.c

@ -55,9 +55,8 @@ httpResponse404()
hashAdd(message->header,
new(HttpHeader, CSTRA("Content-Type"), CSTRA("text/html")));
message->type = HTTP_MESSAGE_BUFFERED;
message->nbody = sizeof(RESP_DATA) - 1;
message->body = malloc(sizeof(RESP_DATA));
message->body = memMalloc(sizeof(RESP_DATA));
memcpy(message->body, RESP_DATA, sizeof(RESP_DATA));
return response;

65
src/http/response/500.c

@ -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:

57
src/http/response/asset.c

@ -1,5 +1,18 @@
/**
* \file
* A response class that delivers an asset (file on disk).
*
* In future this will use a asset class, get from an asset class
* hash. The asset hash will be a shared resource between all
* workers.
*
* The asset class holds an open file descriptor wich is memory
* mapped and is able to give the correct pointer to neede data.
*
* This change will envolve changes in other parts of the response
* write system, as we no longer need to destinguish between piped
* and bufferd resources...we will allways work with a memory address
* only one time its allocated and one time a memory mapped file.
*
* \author Georg Hopp
*
@ -21,6 +34,7 @@
*/
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
@ -37,48 +51,35 @@
#include "utils/memory.h"
#include "hash.h"
#include "asset.h"
HttpResponse
httpResponseAsset(
const char * fname,
const char * mime,
size_t nmime,
const char * match,
size_t nmatch)
httpResponseAsset(const char * fname, size_t nfname)
{
struct tm * tmp;
char etag[200];
size_t netag;
char mtime[200];
size_t nmtime;
struct stat st;
HttpResponse response;
HttpMessage message;
int handle;
handle = open(fname, O_RDONLY);
fstat(handle, &st);
tmp = localtime(&(st.st_mtime));
netag = strftime(etag, sizeof(etag), "%s", tmp);
nmtime = strftime(mtime, sizeof(mtime), "%a, %d %b %Y %T %Z", tmp);
Asset asset = assetPoolGet(fname, nfname);
if (netag == nmatch && 0 == memcmp(etag, match, netag)) {
return httpResponse304(mime, nmime, etag, netag, mtime, nmtime);
if (NULL == asset) {
return NULL;
}
response = new(HttpResponse, "HTTP/1.1", 200, "OK");
message = (HttpMessage)response;
message->type = HTTP_MESSAGE_PIPED;
message->handle = new(Stream, STREAM_FD, handle);
message->nbody = st.st_size;
message->asset = asset;
message->body = asset->data;
message->nbody = asset->size;
hashAdd(message->header,
new(HttpHeader, CSTRA("Content-Type"), mime, nmime));
new(HttpHeader, CSTRA("Content-Type"),
asset->mime_type, asset->nmime_type));
hashAdd(message->header,
new(HttpHeader, CSTRA("ETag"), etag, netag));
new(HttpHeader, CSTRA("ETag"), asset->etag, asset->netag));
hashAdd(message->header,
new(HttpHeader, CSTRA("Last-Modified"), mtime, nmtime));
new(HttpHeader, CSTRA("Last-Modified"),
asset->mtime, asset->nmtime));
return response;
}

4
src/http/response/login_form.c

@ -52,10 +52,8 @@ httpResponseLoginForm()
hashAdd(message->header,
new(HttpHeader, CSTRA("Content-Type"), CSTRA("text/html")));
message->type = HTTP_MESSAGE_BUFFERED;
message->nbody = sizeof(RESP_DATA)-1;
message->body = malloc(message->nbody);
message->body = memMalloc(message->nbody);
memcpy(message->body, RESP_DATA, message->nbody);
return response;

4
src/http/response/randval.c

@ -52,14 +52,12 @@ httpResponseRandval(time_t ctime, int value)
hashAdd(message->header,
new(HttpHeader, CSTRA("Content-Type"), CSTRA("application/json")));
message->type = HTTP_MESSAGE_BUFFERED;
remaining = 10 - (time(NULL) - ctime);
nbuf = sprintf(buffer, RESP_DATA, ctime, remaining, value);
message->nbody = nbuf;
message->body = malloc(nbuf);
message->body = memMalloc(nbuf);
memcpy(message->body, buffer, nbuf);
return response;

4
src/http/response/session.c

@ -52,8 +52,6 @@ httpResponseSession(Session session)
hashAdd(message->header,
new(HttpHeader, CSTRA("Content-Type"), CSTRA("application/json")));
message->type = HTTP_MESSAGE_BUFFERED;
nbuf = sprintf(buffer, RESP_DATA,
(NULL != session)? session->id : 0,
(NULL != session)? SESSION_LIVETIME : 0,
@ -61,7 +59,7 @@ httpResponseSession(Session session)
(NULL != session)? session->username : "");
message->nbody = nbuf;
message->body = malloc(nbuf);
message->body = memMalloc(nbuf);
memcpy(message->body, buffer, nbuf);
return response;

39
src/http/worker.c

@ -24,12 +24,15 @@
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <search.h>
#include "class.h"
#include "stream.h"
#include "hash.h"
#include "http/worker.h"
#include "http/parser.h"
#include "http/writer.h"
@ -44,19 +47,18 @@ httpWorkerCtor(void * _this, va_list * params)
char * id = va_arg(*params, char *);
char cbuf_id[100];
this->id = malloc(strlen(id) + 1);
this->id = memMalloc(strlen(id) + 1);
strcpy(this->id, id);
this->val = va_arg(*params, struct randval *);
this->asset_pool = new(Hash);
sprintf(cbuf_id, "%s_%s", "parser", id);
this->pbuf = new(Cbuf, cbuf_id, PARSER_MAX_BUF);
sprintf(cbuf_id, "%s_%s", "writer", id);
this->wbuf = new(Cbuf, cbuf_id, WRITER_MAX_BUF);
this->parser = new(HttpParser, this->pbuf);
this->writer = new(HttpWriter, this->wbuf);
this->writer = new(HttpWriter);
this->sroot = &(this->session);
this->auth = va_arg(* params, void *);
@ -78,14 +80,14 @@ httpWorkerDtor(void * _this)
{
HttpWorker this = _this;
FREE(this->id);
MEM_FREE(this->id);
delete(this->parser);
delete(this->writer);
if (NULL != this->pbuf) {
delete(this->asset_pool);
delete(this->pbuf); //!< cloned workers have NULL, so delete won't do anything
delete(this->wbuf); //!< cloned workers have NULL, so delete won't do anything
tdestroy(*(this->sroot), tDelete);
}
}
@ -99,8 +101,29 @@ httpWorkerClone(void * _this, void * _base)
this->val = base->val;
this->asset_pool = base->asset_pool;
this->parser = new(HttpParser, base->pbuf);
this->writer = new(HttpWriter, base->wbuf);
/*
* I am pretty sure that it is not neccessary to have a
* separeate writer for each connection...
* Right now I leave it that way.
* TODO check this.
* OK some facts:
* - the stream as well as the worker are associated
* to the filehandle within the server.
* - the response queue is located within the writer.
* (this might be wrong...the response queue should
* be part of the worker. That way I could give it
* into the writer when writing. That way only one
* instance of the writer might be possible...)
* NO, the previous statement is wrong...this would
* involve much more organization overhead within
* the writer...queue change and such...
* At the end I think it might be best to leave it as
* it is.
*/
this->writer = new(HttpWriter);
this->sroot = &(base->session);
this->auth = base->auth;

40
src/http/worker/get_asset.c

@ -26,20 +26,23 @@
#include "http/message.h"
#include "http/request.h"
#include "http/response.h"
#include "http/worker.h"
#include "utils/memory.h"
#include "hash.h"
HttpMessage
httpWorkerGetAsset(
HttpRequest request,
const char * fname,
const char * mime,
size_t nmime)
HttpWorker this,
HttpRequest request,
const char * fname)
{
char * match;
size_t nmatch;
HttpHeader header;
char * match;
size_t nmatch;
HttpHeader header;
HttpMessage message;
size_t nfname = strlen(fname);
header = hashGet(
((HttpMessage)request)->header,
@ -54,8 +57,27 @@ httpWorkerGetAsset(
nmatch = (header->nvalue)[0];
}
return (HttpMessage)httpResponseAsset(
fname, mime, nmime, match, nmatch);
message = (HttpMessage)httpResponseAsset(fname, nfname);
if (NULL == message) {
return (HttpMessage)httpResponse404();
}
if (message->asset->netag == nmatch
&& 0 == memcmp(message->asset->etag, match, nmatch)) {
HttpMessage new_message;
new_message = (HttpMessage)httpResponse304(
message->asset->mime_type, message->asset->nmime_type,
message->asset->etag, message->asset->netag,
message->asset->mtime, message->asset->nmtime);
delete(message);
return new_message;
}
return message;
}
// vim: set ts=4 sw=4:

101
src/http/worker/process.c

@ -42,23 +42,27 @@
#include "http/parser.h"
#include "utils/memory.h"
#include "utils/mime_type.h"
#include "commons.h"
HttpMessage httpWorkerGetAsset(HttpRequest, const char *, const char *, size_t);
HttpMessage httpWorkerGetAsset(HttpWorker, HttpRequest, const char *);
void httpWorkerAddCommonHeader(HttpMessage, HttpMessage);
char * httpWorkerGetMimeType(HttpWorker, const char * extension);
ssize_t
httpWorkerProcess(HttpWorker this, Stream st)
{
ssize_t size;
ssize_t requests = httpParserParse(this->parser, st);
if (0 < (size = httpParserParse(this->parser, st))) {
if (0 > requests) {
return requests;
}
if (0 < requests) {
while (! queueEmpty(this->parser->queue)) {
HttpRequest request = queueGet(
this->parser->queue);
HttpRequest request = queueGet(this->parser->queue);
HttpMessage response = NULL;
/**
@ -69,11 +73,11 @@ httpWorkerProcess(HttpWorker this, Stream st)
if (NULL == this->session) {
HashValue sidstr = hashGet(request->cookies, CSTRA("sid"));
if (NULL != sidstr) {
unsigned long sid;
if (NULL != sidstr) {
unsigned long sid;
sid = strtoul((char*)(sidstr->value), NULL, 10);
this->session = sessionGet(this->sroot, sid);
this->session = sessionGet(this->sroot, sid);
}
}
@ -120,13 +124,13 @@ httpWorkerProcess(HttpWorker this, Stream st)
response = new(HttpResponse, "HTTP/1.1", 403, "Forbidden");
} else {
if (NULL == this->session) {
this->session = sessionAdd(
this->sroot,
this->session = sessionAdd(
this->sroot,
new(Session,
username->value,
username->nvalue));
} else {
this->session->username = malloc(username->nvalue + 1);
this->session->username = memMalloc(username->nvalue + 1);
this->session->username[username->nvalue] = 0;
memcpy(this->session->username,
username->value,
@ -151,19 +155,11 @@ httpWorkerProcess(HttpWorker this, Stream st)
}
if (0 == strcmp("GET", request->method)) {
if (0 == strcmp("/", request->path)) {
response = httpWorkerGetAsset(
request,
"./assets/html/main.html",
CSTRA("text/html"));
}
if (0 == strcmp("/sessinfo/", request->path)) {
response = (HttpMessage)httpResponseSession(this->session);
}
if (0 == strcmp("/sess/", request->path)) {
else if (0 == strcmp("/sess/", request->path)) {
if (NULL == this->session) {
this->session = sessionAdd(
this->sroot,
@ -172,7 +168,7 @@ httpWorkerProcess(HttpWorker this, Stream st)
response = (HttpMessage)httpResponseSession(this->session);
}
if (0 == strcmp("/randval/", request->path)) {
else if (0 == strcmp("/randval/", request->path)) {
if (NULL != this->session) {
response = (HttpMessage)httpResponseRandval(
this->val->timestamp,
@ -182,47 +178,36 @@ httpWorkerProcess(HttpWorker this, Stream st)
}
}
if (0 == strcmp("/image/me", request->path)) {
response = httpWorkerGetAsset(
request,
"./assets/image/waldschrat.jpg",
CSTRA("image/jpeg"));
}
else {
char html_asset[2048] = "./assets/html";
char base_asset[2048] = "./assets";
char main_asset[] = "/main.html";
if (0 == strcmp("/assets/js/jquery", request->path)) {
response = httpWorkerGetAsset(
request,
"./assets/js/jquery-1.7.1.min.js",
CSTRA("text/javascript"));
}
char * asset_path = base_asset;
char * asset;
char * mime_type;
if (0 == strcmp("/assets/js/serverval", request->path)) {
response = httpWorkerGetAsset(
request,
"./assets/js/serverval.js",
CSTRA("text/javascript"));
}
if (0 == strcmp("/", request->path)) {
asset = main_asset;
} else {
asset = request->path;
}
if (0 == strcmp("/assets/js/session", request->path)) {
response = httpWorkerGetAsset(
request,
"./assets/js/session.js",
CSTRA("text/javascript"));
}
mime_type = strrchr(asset, '.');
if (NULL != mime_type) {
mime_type++;
mime_type = getMimeType(mime_type, strlen(mime_type));
}
if (0 == strcmp("/assets/js/init", request->path)) {
response = httpWorkerGetAsset(
request,
"./assets/js/init.js",
CSTRA("text/javascript"));
}
if (NULL != mime_type &&
0 == memcmp(mime_type, CSTRA("text/html"))) {
asset_path = html_asset;
}
if (0 == strcmp("/assets/style/common", request->path)) {
response = httpWorkerGetAsset(
request,
"./assets/style/common.css",
CSTRA("text/css"));
strcat(asset_path, asset);
response = httpWorkerGetAsset(this, request, asset_path);
}
}
if (NULL == response) {
@ -230,15 +215,13 @@ httpWorkerProcess(HttpWorker this, Stream st)
}
httpWorkerAddCommonHeader((HttpMessage)request, response);
delete(request);
queuePut(this->writer->queue, response);
response = NULL;
}
}
return size;
return this->writer->queue->nmsg;
}
// vim: set ts=4 sw=4:

13
src/http/writer.c

@ -28,14 +28,15 @@
#include "queue.h"
#include "http/writer.h"
#include "utils/memory.h"
static
int
httpWriterCtor(void * _this, va_list * params)
{
HttpWriter this = _this;
this->buffer = va_arg(*params, Cbuf);
this->queue = new(Queue);
this->queue = new(Queue);
return 0;
}
@ -48,11 +49,13 @@ httpWriterDtor(void * _this)
delete(this->queue);
if (TRUE == this->ourLock)
cbufRelease(this->buffer);
if (NULL != this->buffer) {
MEM_FREE(this->buffer);
}
if (NULL != this->current)
if (NULL != this->current) {
delete(this->current);
}
}
INIT_IFACE(Class, httpWriterCtor, httpWriterDtor, NULL);

141
src/http/writer/write.c

@ -27,10 +27,10 @@
#include "http/message.h"
#include "queue.h"
#include "http/writer.h"
#include "cbuf.h"
#include "stream.h"
#include "commons.h"
#include "utils/memory.h"
ssize_t
@ -39,112 +39,117 @@ httpWriterWrite(void * _this, Stream st)
HttpWriter this = _this;
int cont = 1;
if (cbufIsLocked(this->buffer)) {
if (FALSE == this->ourLock)
return 0;
}
else {
cbufLock(this->buffer);
this->ourLock = TRUE;
}
while (cont) {
switch (this->state) {
char * start;
ssize_t to_write;
ssize_t written;
case HTTP_WRITER_GET:
if (NULL == this->current && ! queueEmpty(this->queue)) {
if (! queueEmpty(this->queue)) {
this->current = queueGet(this->queue);
this->written = 0;
this->nbody = 0;
this->nheader = httpMessageHeaderSizeGet(this->current);
httpMessageHeaderToString(
this->current,
cbufGetWrite(this->buffer));
cbufIncWrite(this->buffer, this->nheader);
if (this->nheader > memGetSize(this->buffer)) {
ssize_t size = this->nheader;
size = (0 != size%WRITER_BUF_CHUNK)?
(size/WRITER_BUF_CHUNK)+1 :
size/WRITER_BUF_CHUNK;
size *= WRITER_BUF_CHUNK;
if (NULL != this->buffer) {
MEM_FREE(this->buffer);
}
this->buffer = memMalloc(size);
this->nbuffer = size;
}
httpMessageHeaderToString(this->current, this->buffer);
this->nbody = MIN(
this->current->nbody,
this->nbuffer - this->nheader);
memcpy(
this->buffer + this->nheader,
this->current->body,
this->nbody);
this->state = HTTP_WRITER_WRITE;
}
else {
cbufRelease(this->buffer);
this->ourLock = FALSE;
cont = 0;
cont = 0;
break;
}
break;
case HTTP_WRITER_WRITE:
/**
* read
*/
if (this->nbody < this->current->nbody) {
size_t size = MIN(
this->current->nbody - this->nbody,
cbufGetFree(this->buffer));
switch (this->current->type) {
case HTTP_MESSAGE_BUFFERED:
cbufSetData(this->buffer,
this->current->body + this->nbody,
size);
break;
case HTTP_MESSAGE_PIPED:
size = cbufRead(this->buffer, this->current->handle);
break;
default:
return -1;
}
this->nbody += size;
if (this->written >= this->nbuffer) {
size_t body_done = this->written - this->nheader;
start = this->current->body + body_done;
to_write = this->current->nbody - body_done;
} else {
start = this->buffer + this->written;
to_write = (this->nheader + this->nbody) - this->written;
}
/**
* write
*/
{
ssize_t written = cbufWrite(this->buffer, st);
written = streamWrite(st, start, to_write);
if (0 <= written) {
this->written += written;
}
else {
return -1;
}
if (written < 0) {
return written;
}
if (this->written == this->current->nbody + this->nheader) {
this->state = HTTP_WRITER_DONE;
}
else {
this->written += written;
if (written != to_write) {
/*
* for some reason not all data could be
* written...most likely its a slow connection
* so, not to slow down the server we stop
* writing to this one now and come back to
* it in the next run....maybe it would be
* feasable to also implement some kind of
* timeout mechanism for writes...
* By the way, the same is true for reading,
* so to say, the parser.
*/
cont = 0;
break;
}
if (this->written >= this->nheader + this->current->nbody) {
// we are done with this message.
this->state = HTTP_WRITER_DONE;
} else {
break;
}
break;
case HTTP_WRITER_DONE:
this->state = HTTP_WRITER_GET;
cbufRelease(this->buffer);
this->ourLock = FALSE;
if (! httpMessageHasKeepAlive(this->current)) {
/**
* if the message did not have the keep-alive feature
* we don't care about further pipelined messages and
* return to the caller with a -1 indicating that the
* return to the caller with a -2 indicating that the
* underlying connection should be closed at their side.
* Then we close to connection.
*/
delete(this->current);
return -1;
return -2;
}
delete(this->current);
break;
}
}
return this->queue->nmsg;
return NULL == this->current ?
this->queue->nmsg :
this->queue->nmsg + 1;
}
// vim: set ts=4 sw=4:

2
src/logger/Makefile.am

@ -3,4 +3,4 @@ ACLOCAL_AMFLAGS = -I m4
noinst_LIBRARIES = liblogger.a
liblogger_a_SOURCES = interface/i_logger.c logger.c stderr.c syslog.c
liblogger_a_CFLAGS = -Wall -I ../../include/
liblogger_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

31
src/logger/interface/i_logger.c

@ -27,6 +27,8 @@
#include "logger/logger.h"
#include "logger/interface/logger.h"
#include "utils/memory.h"
const struct interface i_Logger = {
"logger",
1
@ -37,23 +39,40 @@ loggerLog(void * _object, logger_level level, const char * const fmt, ...) {
Logger object = _object;
if (level >= object->min_level) {
char * msg = NULL;
size_t msg_size = 0;
va_list params;
char * msg = NULL;
size_t msg_size = 0;
va_list params;
va_start(params, fmt);
msg_size = vsnprintf(msg, msg_size, fmt, params);
msg_size = vsnprintf(NULL, msg_size, fmt, params);
va_end(params);
msg = malloc(msg_size + 1);
msg = memMalloc(msg_size + 1);
va_start(params, fmt);
vsnprintf(msg, msg_size + 1, fmt, params);
va_end(params);
// // ----- DEBUG ------
// do {
// struct i_Logger * iface;
//
// do {
// class_ptr class = GET_CLASS(_object);
// iface = (struct i_Logger *)IFACE_GET(class, &i_Logger);
// while ((NULL == iface || NULL == iface->log) && HAS_PARENT(class)) {
// class = class->parent;
// iface = (struct i_Logger *)IFACE_GET(class, &i_Logger);
// }
// assert(NULL != iface->log);
// } while(0);
//
// iface->log(_object, level, msg);
// } while(0);
// // ----- DEBUG ------
CALL(_object, Logger, log, level, msg);
free(msg);
MEM_FREE(msg);
}
}

55
src/mmapfiletest.c

@ -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:

54
src/mmapfiletest2.c

@ -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:

2
src/queue/Makefile.am

@ -3,4 +3,4 @@ ACLOCAL_AMFLAGS = -I m4
noinst_LIBRARIES = libqueue.a
libqueue_a_SOURCES = queue.c get.c put.c
libqueue_a_CFLAGS = -Wall -I ../../include/
libqueue_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

797
src/rbtree.c

@ -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:

176
src/rbtree2.c

@ -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:

2
src/server/Makefile.am

@ -6,4 +6,4 @@ SERVER = server.c run.c close_conn.c poll.c \
noinst_LIBRARIES = libserver.a
libserver_a_SOURCES = $(SERVER)
libserver_a_CFLAGS = -Wall -I ../../include/
libserver_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

55
src/server/handle_accept.c

@ -45,26 +45,29 @@ serverHandleAccept(Server this, unsigned int i)
acc = socketAccept((0 == i)? this->sock : this->sockSSL, &remoteAddr);
if (-1 != acc->handle) {
switch(i) {
case 0:
// no SSL
st = new(Stream, STREAM_FD, acc->handle);
break;
case 1:
// SSL
{
SSL * ssl = SSL_new(this->ctx);
SSL_set_fd(ssl, acc->handle);
SSL_accept(ssl);
st = new(Stream, STREAM_SSL, ssl);
}
break;
default:
break;
}
if (NULL != acc && -1 != acc->handle) {
socketNonblock(acc);
switch(i) {
case 0:
// no SSL
st = new(Stream, STREAM_FD, acc->handle);
break;
case 1:
// SSL
{
SSL * ssl = SSL_new(this->ctx);
SSL_set_fd(ssl, acc->handle);
SSL_accept(ssl);
st = new(Stream, STREAM_SSL, ssl);
}
break;
default:
st = NULL;
break;
}
// save the socket handle
(this->conns)[acc->handle].sock = acc;
@ -80,21 +83,29 @@ serverHandleAccept(Server this, unsigned int i)
delete(acc);
switch(errno) {
case EAGAIN:
case EAGAIN|EWOULDBLOCK:
case EINTR:
loggerLog(this->logger,
LOGGER_DEBUG,
"server accept blocks");
return -1;
break;
default:
loggerLog(this->logger,
LOGGER_DEBUG,
"server accept error");
return -2;
break;
}
}
return (acc)? acc->handle : -1;
if (0 == this->nfds%200) {
loggerLog(this->logger,
LOGGER_DEBUG, "paralel connections: %lu", this->nfds);
}
return acc->handle;
}
// vim: set ts=4 sw=4:

28
src/server/poll.c

@ -58,21 +58,23 @@ serverPoll(Server this) {
/*
* wait for handles to become ready
*/
if (-1 == (events = poll(this->fds, this->nfds, -1))) {
switch (errno) {
default:
case EBADF:
case EINVAL:
case ENOMEM:
doShutdown = 1;
// DROP THROUGH
do {
if (-1 == (events = poll(this->fds, this->nfds, -1))) {
switch (errno) {
default:
case EBADF:
case EINVAL:
case ENOMEM:
doShutdown = 1;
// DROP THROUGH
case EINTR:
loggerLog(this->logger, LOGGER_CRIT,
"poll systemcall failed: [%s] - service terminated",
strerror(errno));
case EINTR:
loggerLog(this->logger, LOGGER_CRIT,
"poll systemcall failed: [%s] - service terminated",
strerror(errno));
}
}
}
} while (! doShutdown && 0 >= events);
return events;
}

41
src/server/read.c

@ -20,17 +20,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include "server.h"
#include "logger.h"
#include "stream.h"
void serverCloseConn(Server, unsigned int);
ssize_t
serverRead(Server this, unsigned int i)
{
int fd = (this->fds)[i].fd;
ssize_t size;
if (NULL == (this->conns)[fd].worker) {
loggerLog(
@ -40,40 +40,9 @@ serverRead(Server this, unsigned int i)
return -1;
}
switch ((size = streamReaderRead(
(this->conns)[fd].worker,
(this->conns)[fd].stream)))
{
case -2:
/**
* normal close: this must be mapped to -2 within the
* underlying read call.
*
* \todo make sure all pending writes will be done before
* close.
*/
// DROP-THROUGH
case -1:
/*
* read failure / close connection
*/
loggerLog(this->logger, LOGGER_INFO,
"connection[%d] closed...%s",
fd,
inet_ntoa((((this->conns)[fd].sock)->addr).sin_addr));
serverCloseConn(this, i);
break;
case 0:
break;
default:
(this->fds)[i].events |= POLLOUT;
break;
}
return size;
return streamReaderRead(
(this->conns)[fd].worker,
(this->conns)[fd].stream);
}
// vim: set ts=4 sw=4:

90
src/server/run.c

@ -27,68 +27,102 @@
int serverPoll(Server);
int serverHandleAccept(Server, unsigned int);
void serverCloseConn(Server, unsigned int);
ssize_t serverRead(Server, unsigned int);
ssize_t serverWrite(Server, unsigned int);
void serverCloseConn(Server, unsigned int);
void
serverRun(Server this)
{
int events = 0;
loggerLog(this->logger, LOGGER_INFO, "service started");
while (!doShutdown) //! until error or signal
{
int events;
unsigned int i;
int naccs = 10;
events = serverPoll(this);
if (doShutdown || 0 >= events) break;
if (0 <= events) {
/*
* TODO check why sometimes events is less than 0
* There is still a misshandling here.
*/
events = serverPoll(this);
}
/**
* handle accept
*/
if (0 != ((this->fds)[0].revents & POLLIN)) {
events--;
while(-1 != serverHandleAccept(this, 0) && 0 < naccs) {
naccs--;
if (0 > serverHandleAccept(this, 0)) {
events--;
}
}
/**
* handle accept SSL
*/
if (0 != ((this->fds)[1].revents & POLLIN)) {
events--;
while(-1 != serverHandleAccept(this, 1) && 0 < naccs) {
naccs--;
}
}
// /**
// * handle accept SSL
// */
// if (0 != ((this->fds)[1].revents & POLLIN)) {
// if (-1 == serverHandleAccept(this, 1)) {
// events--;
// }
// }
for (i=2; i < this->nfds; i++) {
int nreads = 10, nwrites = 10;
/**
* handle reads
*/
if (0 != ((this->fds)[i].revents & POLLIN) && 0 < nreads) {
events--;
nreads--;
if (0 != ((this->fds)[i].revents & POLLIN)) {
ssize_t processed = serverRead(this, i);
if (0 > processed) {
events--;
switch (processed) {
case -2: // close me...
serverCloseConn(this, i);
serverRead(this, i);
case -1: // poll me again
break;
}
}
if (0 < processed) {
(this->fds)[i].events |= POLLOUT;
}
}
/**
* handle writes
*/
if (0 != ((this->fds)[i].revents & POLLOUT) && 0 < nwrites) {
events--;
nwrites--;
if (0 != ((this->fds)[i].revents & POLLOUT)) {
ssize_t remaining = serverWrite(this, i);
if (0 >= remaining) {
/*
* 0 means queue was empty...try again next
* time...no need to poll again.
* Anyway, most likely we need to read again
* so lets finish this event for now.
*/
events--;
switch (remaining) {
case 0: // nothing more to write stop polling
(this->fds)[i].events &= ~POLLOUT;
break;
case -2: // close me...
serverCloseConn(this, i);
serverWrite(this, i);
case -1: // poll me again
break;
}
}
}
if (0 > events)
if (0 >= events)
break; // no more events to handle
}
}

61
src/server/server.c

@ -61,38 +61,45 @@ serverCtor(void * _this, va_list * params)
port = va_arg(* params, int);
backlog = va_arg(* params, unsigned int);
this->fds = calloc(sizeof(struct pollfd), this->max_fds);
this->conns = calloc(sizeof(struct conns), this->max_fds);
loggerLog(this->logger,
LOGGER_INFO,
"accept up to %zu connections",
this->max_fds);
this->sock = new(Sock, this->logger, port);
flags = fcntl(this->sock->handle, F_GETFL, 0);
this->fds = memCalloc(sizeof(struct pollfd), this->max_fds);
this->conns = memCalloc(sizeof(struct conns), this->max_fds);
this->sock = new(Sock, this->logger, port);
socketNonblock(this->sock);
flags = fcntl(this->sock->handle, F_GETFL, 0);
fcntl(this->sock->handle, F_SETFL, flags | O_NONBLOCK);
this->sockSSL = new(Sock, this->logger, port+1);
flags = fcntl(this->sockSSL->handle, F_GETFL, 0);
fcntl(this->sockSSL->handle, F_SETFL, flags | O_NONBLOCK);
// this->sockSSL = new(Sock, this->logger, port+1);
// flags = fcntl(this->sockSSL->handle, F_GETFL, 0);
// fcntl(this->sockSSL->handle, F_SETFL, flags | O_NONBLOCK);
SSL_library_init();
SSL_load_error_strings();
this->ctx = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_use_certificate_file(
this->ctx,
"./certs/server.crt",
SSL_FILETYPE_PEM);
// SSL_library_init();
// SSL_load_error_strings();
// this->ctx = SSL_CTX_new(SSLv23_server_method());
// SSL_CTX_use_certificate_file(
// this->ctx,
// "./certs/server.crt",
// SSL_FILETYPE_PEM);
SSL_CTX_use_RSAPrivateKey_file(
this->ctx,
"./certs/server.key",
SSL_FILETYPE_PEM);
// SSL_CTX_use_RSAPrivateKey_file(
// this->ctx,
// "./certs/server.key",
// SSL_FILETYPE_PEM);
socketListen(this->sock, backlog);
socketListen(this->sockSSL, backlog);
// socketListen(this->sockSSL, backlog);
(this->fds)[0].fd = this->sock->handle;
(this->fds)[0].events = POLLIN;
(this->fds)[1].fd = this->sockSSL->handle;
(this->fds)[1].events = POLLIN;
this->nfds = 2;
// (this->fds)[1].fd = this->sockSSL->handle;
// (this->fds)[1].events = POLLIN;
// this->nfds = 2;
this->nfds = 1;
return 0;
}
@ -111,14 +118,14 @@ serverDtor(void * _this)
}
}
FREE(this->fds);
FREE(this->conns);
MEM_FREE(this->fds);
MEM_FREE(this->conns);
delete(this->sock);
delete(this->sockSSL);
// delete(this->sockSSL);
SSL_CTX_free(this->ctx);
ERR_free_strings();
// SSL_CTX_free(this->ctx);
// ERR_free_strings();
}
INIT_IFACE(Class, serverCtor, serverDtor, NULL);

24
src/server/write.c

@ -20,44 +20,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include "server.h"
#include "logger.h"
#include "stream.h"
void serverCloseConn(Server, unsigned int);
ssize_t
serverWrite(Server this, unsigned int i)
{
int fd = (this->fds)[i].fd;
ssize_t remaining;
if (NULL == (this->conns)[fd].worker) {
loggerLog(
this->logger,
LOGGER_INFO,
"initialization error: NULL worker");
return -1;
return -2;
}
remaining = streamWriterWrite(
return streamWriterWrite(
(this->conns)[fd].worker,
(this->conns)[fd].stream);
switch(remaining) {
case -1:
serverCloseConn(this, i);
break;
case 0:
(this->fds)[i].events &= ~POLLOUT;
break;
default:
break;
}
return remaining;
}
// vim: set ts=4 sw=4:

2
src/session/Makefile.am

@ -3,4 +3,4 @@ ACLOCAL_AMFLAGS = -I m4
noinst_LIBRARIES = libsession.a
libsession_a_SOURCES = session.c add.c get.c delete.c
libsession_a_CFLAGS = -Wall -I ../../include/
libsession_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

4
src/session/session.c

@ -45,7 +45,7 @@ sessionCtor(void * _this, va_list * params)
this->livetime = time(NULL) + SESSION_LIVETIME;
this->id = sdbm((unsigned char *)uname, nuname) ^ this->livetime;
this->username = malloc(nuname + 1);
this->username = memMalloc(nuname + 1);
this->username[nuname] = 0;
memcpy(this->username, uname, nuname);
@ -58,7 +58,7 @@ sessionDtor(void * _this)
{
Session this = _this;
FREE(this->username);
MEM_FREE(this->username);
}
INIT_IFACE(Class, sessionCtor, sessionDtor, NULL);

4
src/socket/Makefile.am

@ -2,5 +2,5 @@ ACLOCAL_AMFLAGS = -I m4
noinst_LIBRARIES = libsocket.a
libsocket_a_SOURCES = socket.c accept.c connect.c listen.c
libsocket_a_CFLAGS = -Wall -I ../../include/
libsocket_a_SOURCES = socket.c accept.c connect.c listen.c nonblock.c
libsocket_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

12
src/socket/accept.c

@ -22,6 +22,7 @@
#include <errno.h> // for errno
#include <unistd.h>
#include <fcntl.h>
#include "socket.h"
#include "class.h"
@ -36,13 +37,6 @@ socketAccept(Sock this, char (*remoteAddr)[16])
// Set the size of the in-out parameter
len = sizeof(this->addr);
/**
* \todo Uhh, this is bad. we open a new socket additionally to
* the one we get from the accept call.
* i have to change the socket constructor to be able to create
* the data structure without creation of a socket at all.
* For now i simply close the socket here.... :D
*/
sock = new(Sock, this->log, -1);
// Wait for a client to connect
@ -53,8 +47,8 @@ socketAccept(Sock this, char (*remoteAddr)[16])
} else {
strcpy(*remoteAddr, inet_ntoa((sock->addr).sin_addr));
loggerLog(this->log, LOGGER_INFO,
"handling client %s\n", inet_ntoa((sock->addr).sin_addr));
//loggerLog(this->log, LOGGER_INFO,
// "handling client %s\n", inet_ntoa((sock->addr).sin_addr));
}
return sock;

32
src/cbuf/write.c → src/socket/nonblock.c

@ -20,33 +20,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <errno.h> // for errno
#include <unistd.h>
#include <fcntl.h>
#include "cbuf.h"
#include "stream.h"
#include "socket.h"
#include "class.h"
#include "logger.h"
ssize_t
cbufWrite(Cbuf this, Stream st)
void
socketNonblock(Sock this)
{
ssize_t wwsize = 0;
size_t wsize = this->bused;
if (0 == wsize) return 0;
wwsize = streamWrite(st, cbufGetRead(this), wsize);
switch (wwsize) {
case -1:
break;
default:
cbufIncRead(this, wwsize);
break;
}
return wwsize;
int flags = fcntl(this->handle, F_GETFL, 0);
fcntl(this->handle, F_SETFL, flags | O_NONBLOCK);
}
// vim: set ts=4 sw=4:

2
src/stream/Makefile.am

@ -7,4 +7,4 @@ IFACE = interface/reader.c \
noinst_LIBRARIES = libstream.a
libstream_a_SOURCES = $(STREAM) $(IFACE)
libstream_a_CFLAGS = -Wall -I ../../include/
libstream_a_CFLAGS = $(CFLAGS) -Wall -I ../../include/

26
src/stream/read.c

@ -22,6 +22,7 @@
#include <openssl/ssl.h>
#include <unistd.h>
#include <errno.h>
#include "stream.h"
@ -31,8 +32,30 @@ streamRead(Stream this, void * buf, size_t count)
ssize_t done;
switch(this->type) {
ssize_t _read;
case STREAM_FD:
done = read((this->handle).fd, buf, count);
_read = read((this->handle).fd, buf, count);
if (_read < 0) {
switch (errno) {
case EINTR:
case ENOMEM:
done = 0;
break;
case (EAGAIN|EWOULDBLOCK):
done = -1;
break;
default:
done = -2;
break;
}
} else if (_read == 0) {
done = -2;
} else {
done = _read;
}
break;
case STREAM_SSL:
@ -40,6 +63,7 @@ streamRead(Stream this, void * buf, size_t count)
break;
default:
done = 0;
break;
}

25
src/stream/write.c

@ -22,6 +22,7 @@
#include <openssl/ssl.h>
#include <unistd.h>
#include <errno.h>
#include "stream.h"
@ -31,8 +32,29 @@ streamWrite(Stream this, void * buf, size_t count)
ssize_t done;
switch(this->type) {
ssize_t written;
case STREAM_FD:
done = write((this->handle).fd, buf, count);
written = write((this->handle).fd, buf, count);
if (written < 0) {
switch (errno) {
case EINTR:
case ENOBUFS:
case ENOMEM:
done = 0;
break;
case (EAGAIN|EWOULDBLOCK):
done = -1;
break;
default:
done = -2;
break;
}
} else {
done = written;
}
break;
case STREAM_SSL:
@ -40,6 +62,7 @@ streamWrite(Stream this, void * buf, size_t count)
break;
default:
done = 0;
break;
}

14
src/taskrambler.c

@ -45,6 +45,7 @@
#include "utils/signalHandling.h"
#include "utils/memory.h"
#include "utils/mime_type.h"
#define DEFAULT_SECS 10
//#define DEFAULT_USECS (1000000 / HZ * 2)
@ -72,9 +73,12 @@ main()
setrlimit(RLIMIT_NOFILE, &limit);
init_signals();
//daemonize();
shm = shm_open("/fooshm", O_RDWR|O_CREAT, S_IRWXU);
ftruncate(shm, psize);
shm = shm_open("/fooshm", O_RDWR|O_CREAT, S_IRWXU);
if (-1 == ftruncate(shm, psize)) {
doShutdown = 1;
}
switch((pid = fork())) {
case -1:
@ -122,6 +126,7 @@ main()
sigsuspend(&pause_mask);
}
memCleanup();
_exit(EXIT_SUCCESS);
}
@ -145,7 +150,6 @@ main()
worker = new(HttpWorker, "testserver", value, auth);
server = new(Server, logger, worker, 11212, SOMAXCONN);
//daemonize();
if (NULL != server) {
serverRun(server);
}
@ -192,6 +196,10 @@ main()
if (NULL != worker) delete(worker);
if (NULL != auth) delete(auth);
if (NULL != logger) delete(logger);
clearMimeTypes();
assetPoolCleanup();
memCleanup();
}
break;

9
src/tree/Makefile.am

@ -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/

249
src/tree/delete.c

@ -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:

85
src/tree/destroy.c

@ -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

Loading…
Cancel
Save