├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile.am ├── README.md ├── configure.ac ├── m4 └── ax_pthread.m4 ├── man ├── hashbase.1 ├── hashbase.1.html └── hashbase.ronn ├── screenshot-1.png ├── screenshot-2.png ├── src ├── Makefile.am ├── hb.c ├── hb_args.c ├── hb_args.h ├── hb_ascii.c ├── hb_ascii.h ├── hb_core.c ├── hb_core.h ├── hb_map.c ├── hb_map.h ├── hb_net.c ├── hb_net.h ├── hb_pipe.c ├── hb_pipe.h ├── hb_util.c └── hb_util.h └── tests ├── Python ├── hashbase.py └── tests.py └── client.py /.gitignore: -------------------------------------------------------------------------------- 1 | autom4te.cache/* 2 | 3 | build/* 4 | 5 | src/.deps/* 6 | src/hashbase 7 | src/Makefile 8 | src/Makefile.in 9 | 10 | aclocal.m4 11 | config.log 12 | config.status 13 | configure 14 | 15 | Makefile 16 | Makefile.in 17 | 18 | *.o -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | script: autoreconf -fvi && ./configure && make -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Maciej A. Czyzewski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | ACLOCAL_AMFLAGS = -I m4 3 | man1_MANS = man/hashbase.1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | # hashbase [![Build Status](https://travis-ci.org/MaciejCzyzewski/hashbase.png)](https://travis-ci.org/MaciejCzyzewski/hashbase) 6 | 7 | Flexible hackable-storage. 8 | 9 | ## Introduction 10 | 11 | It's a fast, efficient on-disk/in-memory database with many different kind of data structures. 12 | 13 | Hashbase is written in ANSI C comes with a broad range of features including metrics, multi-client API and rich advanced data structure (KV, List, Hash, ZSet, Bit). 14 | 15 | #### More reading: 16 | 17 | - [Installation](#installation): Step-by-step instructions for getting hashbase running on your computer. 18 | - [Usage](#usage): List of commands. 19 | - [Contributing](#contributing): Explanation of how you can join the project. 20 | - [License](#license): Clarification of certain rules. 21 | 22 | ## Installation 23 | 24 | When an archive file of hashbase is extracted, change the current working directory to the generated directory and perform installation. 25 | 26 | Run the configuration script. 27 | 28 | ```bash 29 | $ autoreconf -fvi 30 | $ ./configure 31 | ``` 32 | 33 | Build programs. 34 | 35 | ```bash 36 | $ make 37 | ``` 38 | 39 | Perform self-diagnostic test. This takes a while. 40 | 41 | ```bash 42 | $ make check 43 | ``` 44 | 45 | Install programs. This operation must be carried out by the root user. 46 | 47 | ```bash 48 | $ make install 49 | ``` 50 | 51 | ## Usage 52 | 53 |
54 | 55 |
56 | 57 | This script is the primary interface for starting and stopping the hashbase server. 58 | 59 | ### Start 60 | 61 | To start a daemonized (background) instance of hashbase. 62 | 63 | ```bash 64 | $ hashbase -d 65 | ``` 66 | 67 | ### Console 68 | 69 | Alternatively, if you want to run a foreground instance of hashbase. 70 | 71 | ```bash 72 | $ hashbase 73 | ``` 74 | 75 | ### Stop 76 | 77 | Stopping a foreground or background instance of hashbase can be done from a shell prompt. 78 | 79 | ```bash 80 | $ hashbase -s 81 | ``` 82 | 83 | ### Help 84 | 85 | Displays a brief summary of the basic options. 86 | 87 | ```bash 88 | $ hashbase -h 89 | ``` 90 | 91 | ## Contributing 92 | 93 | Please feel free to contribute to this project! Pull requests and feature requests welcome! :v: 94 | 95 | ## License 96 | 97 | See LICENSE file in this repository. -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Define the package version numbers and the bug reporting address 2 | m4_define([HB_MAJOR], 0) 3 | m4_define([HB_MINOR], 0) 4 | m4_define([HB_PATCH], 1) 5 | m4_define([HB_BUGS], [maciejanthonyczyzewski@gmail.com]) 6 | 7 | # Include m4 macros 8 | m4_include([m4/ax_pthread.m4]) 9 | 10 | # Initialize autoconf 11 | AC_PREREQ([2.64]) 12 | AC_INIT([hashbase], [HB_MAJOR.HB_MINOR.HB_PATCH], [HB_BUGS]) 13 | AC_CONFIG_SRCDIR([src/hb.c]) 14 | AC_CONFIG_AUX_DIR([build]) 15 | AC_CONFIG_MACRO_DIR([m4]) 16 | AC_CANONICAL_SYSTEM 17 | 18 | # Initialize automake 19 | AM_INIT_AUTOMAKE(1.9 foreign) 20 | AM_SILENT_RULES([yes]) 21 | 22 | # Define macro variables for the package version numbers 23 | AC_DEFINE(HB_VERSION_MAJOR, HB_MAJOR, [Define the major version number]) 24 | AC_DEFINE(HB_VERSION_MINOR, HB_MINOR, [Define the minor version number]) 25 | AC_DEFINE(HB_VERSION_PATCH, HB_PATCH, [Define the patch version number]) 26 | AC_DEFINE(HB_VERSION_STRING, "HB_MAJOR.HB_MINOR.HB_PATCH", [Define the version string]) 27 | 28 | # Checks for language 29 | AC_LANG([C]) 30 | 31 | # Checks for programs 32 | AC_PROG_CC 33 | AC_PROG_INSTALL 34 | AC_PROG_MAKE_SET 35 | AM_PROG_CC_C_O 36 | 37 | # Define Makefiles 38 | AC_CONFIG_FILES([Makefile src/Makefile]) 39 | 40 | # Pthread 41 | AX_PTHREAD([AC_DEFINE(HAVE_PTHREAD, 1, [Define if you have POSIX threads libraries and header files.]) 42 | CLIBS="$PTHREAD_LIBS $LIBS" 43 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 44 | LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS" 45 | CC="$PTHREAD_CC"], []) 46 | 47 | # Generate the "configure" script 48 | AC_OUTPUT 49 | -------------------------------------------------------------------------------- /m4/ax_pthread.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_pthread.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # This macro figures out how to build C programs using POSIX threads. It 12 | # sets the PTHREAD_LIBS output variable to the threads library and linker 13 | # flags, and the PTHREAD_CFLAGS output variable to any special C compiler 14 | # flags that are needed. (The user can also force certain compiler 15 | # flags/libs to be tested by setting these environment variables.) 16 | # 17 | # Also sets PTHREAD_CC to any special C compiler that is needed for 18 | # multi-threaded programs (defaults to the value of CC otherwise). (This 19 | # is necessary on AIX to use the special cc_r compiler alias.) 20 | # 21 | # NOTE: You are assumed to not only compile your program with these flags, 22 | # but also link it with them as well. e.g. you should link with 23 | # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS 24 | # 25 | # If you are only building threads programs, you may wish to use these 26 | # variables in your default LIBS, CFLAGS, and CC: 27 | # 28 | # LIBS="$PTHREAD_LIBS $LIBS" 29 | # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 30 | # CC="$PTHREAD_CC" 31 | # 32 | # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant 33 | # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name 34 | # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). 35 | # 36 | # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the 37 | # PTHREAD_PRIO_INHERIT symbol is defined when compiling with 38 | # PTHREAD_CFLAGS. 39 | # 40 | # ACTION-IF-FOUND is a list of shell commands to run if a threads library 41 | # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it 42 | # is not found. If ACTION-IF-FOUND is not specified, the default action 43 | # will define HAVE_PTHREAD. 44 | # 45 | # Please let the authors know if this macro fails on any platform, or if 46 | # you have any other suggestions or comments. This macro was based on work 47 | # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help 48 | # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by 49 | # Alejandro Forero Cuervo to the autoconf macro repository. We are also 50 | # grateful for the helpful feedback of numerous users. 51 | # 52 | # Updated for Autoconf 2.68 by Daniel Richard G. 53 | # 54 | # LICENSE 55 | # 56 | # Copyright (c) 2008 Steven G. Johnson 57 | # Copyright (c) 2011 Daniel Richard G. 58 | # 59 | # This program is free software: you can redistribute it and/or modify it 60 | # under the terms of the GNU General Public License as published by the 61 | # Free Software Foundation, either version 3 of the License, or (at your 62 | # option) any later version. 63 | # 64 | # This program is distributed in the hope that it will be useful, but 65 | # WITHOUT ANY WARRANTY; without even the implied warranty of 66 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 67 | # Public License for more details. 68 | # 69 | # You should have received a copy of the GNU General Public License along 70 | # with this program. If not, see . 71 | # 72 | # As a special exception, the respective Autoconf Macro's copyright owner 73 | # gives unlimited permission to copy, distribute and modify the configure 74 | # scripts that are the output of Autoconf when processing the Macro. You 75 | # need not follow the terms of the GNU General Public License when using 76 | # or distributing such scripts, even though portions of the text of the 77 | # Macro appear in them. The GNU General Public License (GPL) does govern 78 | # all other use of the material that constitutes the Autoconf Macro. 79 | # 80 | # This special exception to the GPL applies to versions of the Autoconf 81 | # Macro released by the Autoconf Archive. When you make and distribute a 82 | # modified version of the Autoconf Macro, you may extend this special 83 | # exception to the GPL to apply to your modified version as well. 84 | 85 | #serial 18 86 | 87 | AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) 88 | AC_DEFUN([AX_PTHREAD], [ 89 | AC_REQUIRE([AC_CANONICAL_HOST]) 90 | AC_LANG_PUSH([C]) 91 | ax_pthread_ok=no 92 | 93 | # We used to check for pthread.h first, but this fails if pthread.h 94 | # requires special compiler flags (e.g. on True64 or Sequent). 95 | # It gets checked for in the link test anyway. 96 | 97 | # First of all, check if the user has set any of the PTHREAD_LIBS, 98 | # etcetera environment variables, and if threads linking works using 99 | # them: 100 | if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then 101 | save_CFLAGS="$CFLAGS" 102 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 103 | save_LIBS="$LIBS" 104 | LIBS="$PTHREAD_LIBS $LIBS" 105 | AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) 106 | AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) 107 | AC_MSG_RESULT($ax_pthread_ok) 108 | if test x"$ax_pthread_ok" = xno; then 109 | PTHREAD_LIBS="" 110 | PTHREAD_CFLAGS="" 111 | fi 112 | LIBS="$save_LIBS" 113 | CFLAGS="$save_CFLAGS" 114 | fi 115 | 116 | # We must check for the threads library under a number of different 117 | # names; the ordering is very important because some systems 118 | # (e.g. DEC) have both -lpthread and -lpthreads, where one of the 119 | # libraries is broken (non-POSIX). 120 | 121 | # Create a list of thread flags to try. Items starting with a "-" are 122 | # C compiler flags, and other items are library names, except for "none" 123 | # which indicates that we try without any flags at all, and "pthread-config" 124 | # which is a program returning the flags for the Pth emulation library. 125 | 126 | ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" 127 | 128 | # The ordering *is* (sometimes) important. Some notes on the 129 | # individual items follow: 130 | 131 | # pthreads: AIX (must check this before -lpthread) 132 | # none: in case threads are in libc; should be tried before -Kthread and 133 | # other compiler flags to prevent continual compiler warnings 134 | # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) 135 | # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) 136 | # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) 137 | # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) 138 | # -pthreads: Solaris/gcc 139 | # -mthreads: Mingw32/gcc, Lynx/gcc 140 | # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it 141 | # doesn't hurt to check since this sometimes defines pthreads too; 142 | # also defines -D_REENTRANT) 143 | # ... -mt is also the pthreads flag for HP/aCC 144 | # pthread: Linux, etcetera 145 | # --thread-safe: KAI C++ 146 | # pthread-config: use pthread-config program (for GNU Pth library) 147 | 148 | case ${host_os} in 149 | solaris*) 150 | 151 | # On Solaris (at least, for some versions), libc contains stubbed 152 | # (non-functional) versions of the pthreads routines, so link-based 153 | # tests will erroneously succeed. (We need to link with -pthreads/-mt/ 154 | # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather 155 | # a function called by this macro, so we could check for that, but 156 | # who knows whether they'll stub that too in a future libc.) So, 157 | # we'll just look for -pthreads and -lpthread first: 158 | 159 | ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" 160 | ;; 161 | 162 | darwin*) 163 | ax_pthread_flags="-pthread $ax_pthread_flags" 164 | ;; 165 | esac 166 | 167 | if test x"$ax_pthread_ok" = xno; then 168 | for flag in $ax_pthread_flags; do 169 | 170 | case $flag in 171 | none) 172 | AC_MSG_CHECKING([whether pthreads work without any flags]) 173 | ;; 174 | 175 | -*) 176 | AC_MSG_CHECKING([whether pthreads work with $flag]) 177 | PTHREAD_CFLAGS="$flag" 178 | ;; 179 | 180 | pthread-config) 181 | AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) 182 | if test x"$ax_pthread_config" = xno; then continue; fi 183 | PTHREAD_CFLAGS="`pthread-config --cflags`" 184 | PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" 185 | ;; 186 | 187 | *) 188 | AC_MSG_CHECKING([for the pthreads library -l$flag]) 189 | PTHREAD_LIBS="-l$flag" 190 | ;; 191 | esac 192 | 193 | save_LIBS="$LIBS" 194 | save_CFLAGS="$CFLAGS" 195 | LIBS="$PTHREAD_LIBS $LIBS" 196 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 197 | 198 | # Check for various functions. We must include pthread.h, 199 | # since some functions may be macros. (On the Sequent, we 200 | # need a special flag -Kthread to make this header compile.) 201 | # We check for pthread_join because it is in -lpthread on IRIX 202 | # while pthread_create is in libc. We check for pthread_attr_init 203 | # due to DEC craziness with -lpthreads. We check for 204 | # pthread_cleanup_push because it is one of the few pthread 205 | # functions on Solaris that doesn't have a non-functional libc stub. 206 | # We try pthread_create on general principles. 207 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include 208 | static void routine(void *a) { a = 0; } 209 | static void *start_routine(void *a) { return a; }], 210 | [pthread_t th; pthread_attr_t attr; 211 | pthread_create(&th, 0, start_routine, 0); 212 | pthread_join(th, 0); 213 | pthread_attr_init(&attr); 214 | pthread_cleanup_push(routine, 0); 215 | pthread_cleanup_pop(0) /* ; */])], 216 | [ax_pthread_ok=yes], 217 | []) 218 | 219 | LIBS="$save_LIBS" 220 | CFLAGS="$save_CFLAGS" 221 | 222 | AC_MSG_RESULT($ax_pthread_ok) 223 | if test "x$ax_pthread_ok" = xyes; then 224 | break; 225 | fi 226 | 227 | PTHREAD_LIBS="" 228 | PTHREAD_CFLAGS="" 229 | done 230 | fi 231 | 232 | # Various other checks: 233 | if test "x$ax_pthread_ok" = xyes; then 234 | save_LIBS="$LIBS" 235 | LIBS="$PTHREAD_LIBS $LIBS" 236 | save_CFLAGS="$CFLAGS" 237 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 238 | 239 | # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. 240 | AC_MSG_CHECKING([for joinable pthread attribute]) 241 | attr_name=unknown 242 | for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do 243 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], 244 | [int attr = $attr; return attr /* ; */])], 245 | [attr_name=$attr; break], 246 | []) 247 | done 248 | AC_MSG_RESULT($attr_name) 249 | if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then 250 | AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, 251 | [Define to necessary symbol if this constant 252 | uses a non-standard name on your system.]) 253 | fi 254 | 255 | AC_MSG_CHECKING([if more special flags are required for pthreads]) 256 | flag=no 257 | case ${host_os} in 258 | aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; 259 | osf* | hpux*) flag="-D_REENTRANT";; 260 | solaris*) 261 | if test "$GCC" = "yes"; then 262 | flag="-D_REENTRANT" 263 | else 264 | flag="-mt -D_REENTRANT" 265 | fi 266 | ;; 267 | esac 268 | AC_MSG_RESULT(${flag}) 269 | if test "x$flag" != xno; then 270 | PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" 271 | fi 272 | 273 | AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], 274 | ax_cv_PTHREAD_PRIO_INHERIT, [ 275 | AC_LINK_IFELSE([ 276 | AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], 277 | [ax_cv_PTHREAD_PRIO_INHERIT=yes], 278 | [ax_cv_PTHREAD_PRIO_INHERIT=no]) 279 | ]) 280 | AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], 281 | AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) 282 | 283 | LIBS="$save_LIBS" 284 | CFLAGS="$save_CFLAGS" 285 | 286 | # More AIX lossage: must compile with xlc_r or cc_r 287 | if test x"$GCC" != xyes; then 288 | AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) 289 | else 290 | PTHREAD_CC=$CC 291 | fi 292 | else 293 | PTHREAD_CC="$CC" 294 | fi 295 | 296 | AC_SUBST(PTHREAD_LIBS) 297 | AC_SUBST(PTHREAD_CFLAGS) 298 | AC_SUBST(PTHREAD_CC) 299 | 300 | # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: 301 | if test x"$ax_pthread_ok" = xyes; then 302 | ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) 303 | : 304 | else 305 | ax_pthread_ok=no 306 | $2 307 | fi 308 | AC_LANG_POP 309 | ])dnl AX_PTHREAD -------------------------------------------------------------------------------- /man/hashbase.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "HASHBASE" "1" "July 2014" "" "" 5 | . 6 | .SH "NAME" 7 | \fBhashbase\fR \- flexible hackable\-storage 8 | . 9 | .SH "SYNOPSIS" 10 | \fBhashbase\fR 11 | . 12 | .br 13 | \fBhashbase\fR [\fIoptions\fR\.\.\.] 14 | . 15 | .br 16 | \fBhashbase\fR \fB\-d\fR|\fB\-\-daemonize\fR 17 | . 18 | .br 19 | \fBhashbase\fR \fB\-\-port\fR=\fINUMBER\fR 20 | . 21 | .br 22 | . 23 | .SH "DESCRIPTION" 24 | It\'s a fast, efficient on\-disk/in\-memory database with many different kind of data structures\. 25 | . 26 | .SH "OPTIONS" 27 | . 28 | .TP 29 | \fB\-d\fR, \fB\-\-daemonize\fR 30 | Run in the background as daemon\. 31 | . 32 | .TP 33 | \fB\-s\fR, \fB\-\-stop\fR 34 | Close running daemon\. 35 | . 36 | .TP 37 | \fB\-p\fR=\fINUMBER\fR, \fB\-\-port\fR=\fINUMBER\fR 38 | Set port for server\. 39 | . 40 | .TP 41 | \fB\-v\fR, \fB\-\-version\fR 42 | Show hashbase version and exit\. 43 | . 44 | .TP 45 | \fB\-h\fR, \fB\-\-help\fR 46 | Show help and exit\. 47 | . 48 | .SH "EXAMPLES" 49 | Serve TCP server with debug logging: 50 | . 51 | .IP "" 4 52 | . 53 | .nf 54 | 55 | $ hashbase 56 | . 57 | .fi 58 | . 59 | .IP "" 0 60 | . 61 | .P 62 | Serve TCP server at localhost:1207 as daemon: 63 | . 64 | .IP "" 4 65 | . 66 | .nf 67 | 68 | $ hashbase \-d \-p 1207 69 | . 70 | .fi 71 | . 72 | .IP "" 0 73 | . 74 | .P 75 | Close server: 76 | . 77 | .IP "" 4 78 | . 79 | .nf 80 | 81 | $ hashbase \-s 82 | . 83 | .fi 84 | . 85 | .IP "" 0 86 | . 87 | .SH "BUGS" 88 | \fBHashbase\fR is written in ANSI C and depends on autoconf and extension libraries that are non\-trivial to install on some systems\. A more portable version of this program would be welcome\. 89 | . 90 | .SH "COPYRIGHT" 91 | Hashbase is Copyright (C) 2014 Maciej A\. Czyzewski 92 | . 93 | .SH "SEE ALSO" 94 | man(1), manpages(5) 95 | -------------------------------------------------------------------------------- /man/hashbase.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | hashbase(1) - flexible hackable-storage 7 | 44 | 45 | 52 | 53 |
54 | 55 | 65 | 66 |
    67 |
  1. hashbase(1)
  2. 68 |
  3. 69 |
  4. hashbase(1)
  5. 70 |
71 | 72 |

NAME

73 |

74 | hashbase - flexible hackable-storage 75 |

76 | 77 |

SYNOPSIS

78 | 79 |

hashbase
80 | hashbase [options...]
81 | hashbase -d|--daemonize
82 | hashbase --port=NUMBER

83 | 84 |

DESCRIPTION

85 | 86 |

It's a fast, efficient on-disk/in-memory database with many different kind of 87 | data structures.

88 | 89 |

OPTIONS

90 | 91 |
92 |
-d, --daemonize

Run in the background as daemon.

93 |
-s, --stop

Close running daemon.

94 |
-p=NUMBER, --port=NUMBER

Set port for server.

95 |
-v, --version

Show hashbase version and exit.

96 |
-h, --help

Show help and exit.

97 |
98 | 99 | 100 |

EXAMPLES

101 | 102 |

Serve TCP server with debug logging:

103 | 104 |
$ hashbase
105 | 
106 | 107 |

Serve TCP server at localhost:1207 as daemon:

108 | 109 |
$ hashbase -d -p 1207
110 | 
111 | 112 |

Close server:

113 | 114 |
$ hashbase -s
115 | 
116 | 117 |

BUGS

118 | 119 |

Hashbase is written in ANSI C and depends on autoconf and extension 120 | libraries that are non-trivial to install on some systems. A more portable 121 | version of this program would be welcome.

122 | 123 | 124 | 125 |

Hashbase is Copyright (C) 2014 Maciej A. Czyzewski

126 | 127 |

SEE ALSO

128 | 129 |

man(1), manpages(5)

130 | 131 | 132 |
    133 |
  1. 134 |
  2. July 2014
  3. 135 |
  4. hashbase(1)
  5. 136 |
137 | 138 |
139 | 140 | 141 | -------------------------------------------------------------------------------- /man/hashbase.ronn: -------------------------------------------------------------------------------- 1 | hashbase(1) -- flexible hackable-storage 2 | ======================================== 3 | 4 | ## SYNOPSIS 5 | 6 | `hashbase`
7 | `hashbase` [...]
8 | `hashbase` `-d`|`--daemonize`
9 | `hashbase` `--port`=
10 | 11 | ## DESCRIPTION 12 | 13 | It's a fast, efficient on-disk/in-memory database with many different kind of 14 | data structures. 15 | 16 | ## OPTIONS 17 | 18 | * `-d`, `--daemonize`: 19 | Run in the background as daemon. 20 | 21 | * `-s`, `--stop`: 22 | Close running daemon. 23 | 24 | * `-p`=, `--port`=: 25 | Set port for server. 26 | 27 | * `-v`, `--version`: 28 | Show hashbase version and exit. 29 | 30 | * `-h`, `--help`: 31 | Show help and exit. 32 | 33 | ## EXAMPLES 34 | 35 | Serve TCP server with debug logging: 36 | 37 | $ hashbase 38 | 39 | Serve TCP server at localhost:1207 as daemon: 40 | 41 | $ hashbase -d -p 1207 42 | 43 | Close server: 44 | 45 | $ hashbase -s 46 | 47 | ## BUGS 48 | 49 | **Hashbase** is written in ANSI C and depends on autoconf and extension 50 | libraries that are non-trivial to install on some systems. A more portable 51 | version of this program would be welcome. 52 | 53 | ## COPYRIGHT 54 | 55 | Hashbase is Copyright (C) 2014 Maciej A. Czyzewski 56 | 57 | ## SEE ALSO 58 | 59 | man(1), manpages(5) -------------------------------------------------------------------------------- /screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maciejczyzewski/hashbase/04d7c8cc40dab40ad9870949f16458afaa37a40c/screenshot-1.png -------------------------------------------------------------------------------- /screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maciejczyzewski/hashbase/04d7c8cc40dab40ad9870949f16458afaa37a40c/screenshot-2.png -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = hashbase 2 | 3 | AM_CPPFLAGS = -D_GNU_SOURCE 4 | 5 | AM_CFLAGS = -O2 -Wall 6 | AM_CFLAGS += -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls 7 | AM_CFLAGS += -fno-strict-aliasing 8 | 9 | LDFLAGS += -rdynamic -lpthread 10 | 11 | hashbase_SOURCES = \ 12 | hb_core.c hb_core.h \ 13 | hb_net.c hb_net.h \ 14 | hb_map.c hb_map.h \ 15 | hb_pipe.c hb_pipe.h \ 16 | hb_util.c hb_util.h \ 17 | hb_ascii.c hb_ascii.h \ 18 | hb_args.c hb_args.h \ 19 | hb.c 20 | -------------------------------------------------------------------------------- /src/hb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hashbase - https://github.com/MaciejCzyzewski/hashbase 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2014 Maciej A. Czyzewski 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Author: Maciej A. Czyzewski 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | struct server server; 40 | struct client client; 41 | 42 | map_t database; 43 | 44 | /* ================================ Globals ================================ */ 45 | 46 | int main(int argc , char *argv[]) 47 | { 48 | signal(SIGINT, core_close); 49 | 50 | server.pid = getpid(); 51 | server.lock = HB_CORE_LOCK; 52 | 53 | server.port = HB_NET_PORT; 54 | server.backlog = HB_NET_BACKLOG; 55 | server.buffer = HB_NET_BUFFER; 56 | 57 | server.daemonize = false; 58 | 59 | client.size = sizeof(struct sockaddr_in); 60 | 61 | core_init(argc, argv); 62 | 63 | char *ascii_logo = 64 | " \n" 65 | " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \n" 66 | " XXXXXXXXXXXXXXXXXXXXXXXXXX _ X \n" 67 | " XX|`|XXXXXXXXXXXXX|`|XXXXX | |__ __ _ ___ ___ X \n" 68 | " XX| '_ \\X/`_``/`__| '_`\\XX | '_ \\ / _` / __|/ _ \\ X\n" 69 | " XX| |X| | (X| \\__ \\ |X| XX | |_) | (_| \\__ \\ __/ X\n" 70 | " XX|_|X|_|\\__,_|___/_|X|_XX |_.__/ \\__,_|___/\\___| X \n" 71 | " XXXXXXXXXXXXXXXXXXXXXXXXXX ver %s X \n" 72 | " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \n" 73 | " port: %d, pid: %ld \n\n"; 74 | 75 | printf(ascii_logo, HB_VERSION, server.port, server.pid); 76 | 77 | server.status = net_init(); 78 | if (server.status == HB_ERR) core_close(1); 79 | 80 | server.status = map_init(); 81 | if (server.status == HB_ERR) core_close(1); 82 | 83 | fprintf(stdout, "hb: %s waiting for incoming connections...\n", HB_LOG_INF); 84 | 85 | struct ascii_t commands [] = { 86 | { "inf", ascii_inf }, 87 | { "set", ascii_set }, 88 | { "get", ascii_get }, 89 | { "del", ascii_del }, 90 | { "len", ascii_len }, 91 | { "clr", ascii_clr }, 92 | }; 93 | 94 | server.commands = commands; 95 | 96 | net_loop(); 97 | 98 | return 0; 99 | } -------------------------------------------------------------------------------- /src/hb_args.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ARGS Provides functionality to parse standard argc/argv in an easy manner. 3 | * 4 | * Version: @(#)args.c 0.0.1 09/07/14 5 | * Authors: Maciej A. Czyzewski, 6 | * 7 | */ 8 | 9 | #include /* for vsnprintf */ 10 | #include /* for va_list */ 11 | #include 12 | #if !defined(_MSC_VER) 13 | # include /* for strncasecmp */ 14 | #else 15 | # include /* tolower */ 16 | #endif 17 | 18 | #include 19 | 20 | static int str_case_cmp_len(const char* s1, const char* s2, unsigned int len) 21 | { 22 | #if defined (_MSC_VER) 23 | for(unsigned int i = 0; i < len; i++) { 24 | int c1 = tolower(s1[i]); 25 | int c2 = tolower(s2[i]); 26 | if(c1 < c2) return -1; 27 | if(c1 > c2) return 1; 28 | if(c1 == '\0' && c1 == c2) return 0; 29 | } 30 | return 0; 31 | #else /* defined (_MSC_VER) */ 32 | return strncasecmp(s1, s2, len); 33 | #endif /* defined (_MSC_VER) */ 34 | } 35 | 36 | static int str_format(char* buf, size_t buf_size, const char* fmt, ...) 37 | { 38 | va_list args; 39 | va_start( args, fmt ); 40 | int ret = vsnprintf( buf, buf_size, fmt, args ); 41 | #if defined(_MSC_VER) 42 | buf[buf_size - 1] = '\0'; 43 | #endif 44 | va_end( args ); 45 | return ret; 46 | } 47 | 48 | int args_create_context( args_context_t* ctx, int argc, const char** argv, const args_option_t* opts ) 49 | { 50 | ctx->argc = argc - 1; /* stripping away file-name! */ 51 | ctx->argv = argv + 1; /* stripping away file-name! */ 52 | ctx->opts = opts; 53 | ctx->current_index = 0; 54 | ctx->current_opt_arg = 0x0; 55 | 56 | /* count opts */ 57 | ctx->num_opts = 0; 58 | args_option_t cmp_opt; 59 | memset( &cmp_opt, 0x0, sizeof(args_option_t) ); 60 | const args_option_t* opt = opts; 61 | while( memcmp( opt, &cmp_opt, sizeof(args_option_t) ) != 0 ) { 62 | if( opt->value == '!' || 63 | opt->value == '?' || 64 | opt->value == '+' || 65 | opt->value == 0 || 66 | opt->value == -1) 67 | return -1; 68 | 69 | ctx->num_opts++; 70 | opt++; 71 | } 72 | 73 | ctx->num_opts = (int)(opt - opts); 74 | 75 | return 0; 76 | } 77 | 78 | int args_next( args_context_t* ctx ) 79 | { 80 | /* are all options processed? */ 81 | if(ctx->current_index == ctx->argc ) 82 | return -1; 83 | 84 | /* reset opt-arg */ 85 | ctx->current_opt_arg = 0x0; 86 | 87 | const char* curr_token = ctx->argv[ ctx->current_index ]; 88 | 89 | /* this token has been processed! */ 90 | ctx->current_index++; 91 | 92 | /* check if item is no option */ 93 | if( curr_token[0] && curr_token[0] != '-' ) { 94 | ctx->current_opt_arg = curr_token; 95 | return '+'; /* return '+' as identifier for no option! */ 96 | } 97 | 98 | const args_option_t* found_opt = 0x0; 99 | const char* found_arg = 0x0; 100 | 101 | /* short opt */ 102 | if( curr_token[1] != '\0' && curr_token[1] != '-' && curr_token[2] == '\0' ) { 103 | int i = 0; 104 | for( ; i < ctx->num_opts; i++ ) { 105 | const args_option_t* opt = ctx->opts + i; 106 | 107 | if( opt->name_short == curr_token[1] ) { 108 | found_opt = opt; 109 | 110 | /* if there is an value when: - current_index < argc and value in argv[current_index] do not start with '-' */ 111 | if( ( ( ctx->current_index != ctx->argc) && ( ctx->argv[ctx->current_index][0] != '-' ) ) && 112 | ( opt->type == ARGS_OPTION_TYPE_OPTIONAL || opt->type == ARGS_OPTION_TYPE_REQUIRED ) ) { 113 | found_arg = ctx->argv[ctx->current_index]; 114 | ctx->current_index++; /* next token has been processed aswell! */ 115 | } 116 | break; 117 | } 118 | } 119 | } 120 | /* long opt */ 121 | else if(curr_token[1] == '-' && curr_token[2] != '\0') { 122 | const char* check_option = curr_token + 2; 123 | 124 | int i = 0; 125 | for( ; i < ctx->num_opts; i++ ) { 126 | const args_option_t* opt = ctx->opts + i; 127 | 128 | unsigned int name_len = (unsigned int)strlen( opt->name ); 129 | 130 | if( str_case_cmp_len( opt->name, check_option, name_len ) == 0 ) { 131 | check_option += name_len; 132 | 133 | /* find arg if there is any */ 134 | switch( *check_option ) { 135 | case '\0': { 136 | if( ctx->current_index < ctx->argc ) { /* are there more tokens that can contain the '='? */ 137 | const char* next_token = ctx->argv[ ctx->current_index ]; 138 | if( next_token[0] == '=' ) { 139 | ctx->current_index++; /* next token has been processed aswell! */ 140 | 141 | if( next_token[1] != '\0' ) /* does this token contain the arg-value? */ 142 | found_arg = next_token + 1; 143 | else if( ctx->current_index < ctx->argc ) 144 | found_arg = ctx->argv[ ctx->current_index++ ]; /* next token has been processed aswell! */ 145 | } 146 | } 147 | } 148 | break; 149 | case '=': 150 | if( check_option[1] != '\0' ) 151 | found_arg = check_option + 1; 152 | else if( ctx->current_index < ctx->argc ) 153 | found_arg = ctx->argv[ ctx->current_index++ ]; /* next token has been processed aswell! */ 154 | break; 155 | default: 156 | /* not found, matched for example --test but it was --testing*/ 157 | continue; 158 | } 159 | 160 | found_opt = opt; 161 | break; 162 | } 163 | } 164 | } 165 | /* malformed opt "-", "-xyz" or "--" */ 166 | else { 167 | ctx->current_opt_arg = curr_token; 168 | return '!'; 169 | } 170 | 171 | /* found no matching option! */ 172 | if(found_opt == 0x0) { 173 | ctx->current_opt_arg = curr_token; 174 | return '?'; 175 | } 176 | 177 | if(found_arg != 0x0) { 178 | switch(found_opt->type) { 179 | case ARGS_OPTION_TYPE_FLAG_SET: 180 | case ARGS_OPTION_TYPE_FLAG_AND: 181 | case ARGS_OPTION_TYPE_FLAG_OR: 182 | case ARGS_OPTION_TYPE_NO_ARG: 183 | /* these types should have no argument, usage error! */ 184 | ctx->current_opt_arg = found_opt->name; 185 | return '!'; 186 | 187 | case ARGS_OPTION_TYPE_OPTIONAL: 188 | case ARGS_OPTION_TYPE_REQUIRED: 189 | ctx->current_opt_arg = found_arg; 190 | return found_opt->value; 191 | } 192 | } 193 | /* no argument found */ 194 | else { 195 | switch(found_opt->type) { 196 | case ARGS_OPTION_TYPE_FLAG_SET: 197 | *found_opt->flag = found_opt->value; 198 | return 0; 199 | case ARGS_OPTION_TYPE_FLAG_AND: 200 | *found_opt->flag &= found_opt->value; 201 | return 0; 202 | case ARGS_OPTION_TYPE_FLAG_OR: 203 | *found_opt->flag |= found_opt->value; 204 | return 0; /* zero is "a flag was set!" */ 205 | 206 | case ARGS_OPTION_TYPE_NO_ARG: 207 | case ARGS_OPTION_TYPE_OPTIONAL: 208 | return found_opt->value; 209 | 210 | /* the option requires an argument! (--option=arg, -o arg) */ 211 | case ARGS_OPTION_TYPE_REQUIRED: 212 | ctx->current_opt_arg = found_opt->name; 213 | return '!'; 214 | } 215 | } 216 | 217 | return -1; 218 | } 219 | 220 | const char* args_create_help_string( args_context_t* ctx, char* buffer, size_t buffer_size ) 221 | { 222 | size_t buffer_pos = 0; 223 | int opt_index = 0; 224 | for( ; opt_index < ctx->num_opts; ++opt_index ) { 225 | const args_option_t* opt = ctx->opts + opt_index; 226 | 227 | char long_name[64]; 228 | int chars_written = str_format( long_name, 64, "--%s", opt->name ); 229 | if( chars_written < 0 ) 230 | return buffer; 231 | 232 | size_t outpos = (size_t)chars_written; 233 | 234 | switch( opt->type ) { 235 | case ARGS_OPTION_TYPE_REQUIRED: 236 | str_format(long_name + outpos, 64 - outpos, "=<%s>", opt->value_desc); 237 | break; 238 | case ARGS_OPTION_TYPE_OPTIONAL: 239 | str_format(long_name + outpos, 64 - outpos, "(=%s)", opt->value_desc); 240 | break; 241 | default: 242 | break; 243 | } 244 | 245 | if(opt->name_short == 0x0) 246 | chars_written = str_format( buffer + buffer_pos, buffer_size - buffer_pos, " %-32s - %s\n", long_name, opt->desc ); 247 | else 248 | chars_written = str_format( buffer + buffer_pos, buffer_size - buffer_pos, "-%c %-32s - %s\n", opt->name_short, long_name, opt->desc); 249 | 250 | if( chars_written < 0 ) 251 | return buffer; 252 | 253 | buffer_pos += (size_t)chars_written; 254 | } 255 | 256 | return buffer; 257 | } -------------------------------------------------------------------------------- /src/hb_args.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hashbase - https://github.com/MaciejCzyzewski/hashbase 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2014 Maciej A. Czyzewski 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Author: Maciej A. Czyzewski 26 | */ 27 | 28 | #ifndef _HB_ARGS_H_ 29 | #define _HB_ARGS_H_ 30 | 31 | #include 32 | 33 | /* 34 | Enum: args_option_type_t 35 | Types of supported options by system. 36 | 37 | ARGS_OPTION_TYPE_NO_ARG - The option can have no argument 38 | ARGS_OPTION_TYPE_REQUIRED - The option requires an argument (--option=arg, -o arg) 39 | ARGS_OPTION_TYPE_OPTIONAL - The option-argument is optional 40 | 41 | ARGS_OPTION_TYPE_FLAG_SET - The option is a flag and value will be set to flag 42 | ARGS_OPTION_TYPE_FLAG_AND - The option is a flag and value will be and:ed with flag 43 | ARGS_OPTION_TYPE_FLAG_OR - The option is a flag and value will be or:ed with flag 44 | */ 45 | typedef enum args_option_type { 46 | ARGS_OPTION_TYPE_NO_ARG, 47 | ARGS_OPTION_TYPE_REQUIRED, 48 | ARGS_OPTION_TYPE_OPTIONAL, 49 | ARGS_OPTION_TYPE_FLAG_SET, 50 | ARGS_OPTION_TYPE_FLAG_AND, 51 | ARGS_OPTION_TYPE_FLAG_OR 52 | } args_option_type_t; 53 | 54 | /** 55 | * Helper-macro to define end-element in options-array. 56 | * Mostly helpful on higher warning-level where compiler would complain for { 0 } 57 | */ 58 | #define ARGS_OPTIONS_END { 0, 0, ARGS_OPTION_TYPE_NO_ARG, 0, 0, 0, 0 } 59 | 60 | /* 61 | Struct: args_option 62 | Option in system. 63 | 64 | Members: 65 | name - Long name of argument, set to NULL if only short name is valid. 66 | name_short - Short name of argument, set to 0 if only long name is valid. 67 | type - Type of option, see . 68 | flag - Pointer to flag to set if option is of flag-type, set to null NULL if option is not of flag-type. 69 | value - If option is of flag-type, this value will be set/and:ed/or:ed to the flag, else it will be returned from GetOpt when option is found. 70 | desc - Description of option. 71 | value_desc - Short description of valid values to the option, will only be used when generating help-text. example: "--my_option=" 72 | */ 73 | typedef struct args_option { 74 | const char* name; 75 | int name_short; 76 | args_option_type_t type; 77 | int* flag; 78 | int value; 79 | const char* desc; 80 | const char* value_desc; 81 | } args_option_t; 82 | 83 | /* 84 | Struct: args_context_t 85 | Context used while parsing options. 86 | Need to be initialized by before usage. If reused a re-initialization by is needed. 87 | Do not modify data in this struct manually! 88 | 89 | Members: 90 | argc - Internal variable 91 | argv - Internal variable 92 | opts - Internal variable 93 | num_opts - Internal variable 94 | current_index - Internal variable 95 | current_opt_arg - Used to return values. See 96 | */ 97 | typedef struct args_context { 98 | int argc; 99 | const char** argv; 100 | const args_option_t* opts; 101 | int num_opts; 102 | int current_index; 103 | const char* current_opt_arg; 104 | } args_context_t; 105 | 106 | /* 107 | Function: args_create_context 108 | Initializes an args_context_t-struct to be used by 109 | 110 | Arguments: 111 | ctx - Pointer to context to initialize. 112 | argc - argc from "int main(int argc, char** argv)" or equal. 113 | argv - argv from "int main(int argc, char** argv)" or equal. Data need to be valid during option-parsing and usage of data. 114 | opts - Pointer to array with options that should be looked for. Should end with an option that is all zeroed! 115 | 116 | Returns: 117 | 0 on success, otherwise error-code. 118 | */ 119 | int args_create_context( args_context_t* ctx, int argc, const char** argv, const args_option_t* opts ); 120 | 121 | /* 122 | Function: args_next 123 | Used to parse argc/argv with the help of a args_context_t. 124 | Tries to parse the next token in ctx and return id depending on status. 125 | 126 | Arguments: 127 | ctx - Pointer to a initialized 128 | 129 | Returns: 130 | - '!' on error. ctx->current_opt_arg will be set to flag-name! Errors that can occur, 131 | Argument missing if argument is required or Argument found when there should be none. 132 | - '?' if item was an unrecognized option, ctx->current_opt_arg will be set to item! 133 | - '+' if item was no option, ctx->current_opt_arg will be set to item! 134 | - '0' if the opt was a flag and it was set. ctx->current_opt_arg will be set to flag-name! 135 | the value stored is value in the found option. 136 | - -1 no more options to parse! 137 | */ 138 | int args_next( args_context_t* ctx ); 139 | 140 | /* 141 | Function: GetOptCreateHelpString 142 | Builds a string that describes all options for use with the --help-flag etc. 143 | 144 | Arguments: 145 | ctx - Pointer to a initialized 146 | buffer - Pointer to buffer to build string in. 147 | buffer_size - Size of buffer. 148 | 149 | Returns: 150 | buffer filled with a help-string. 151 | */ 152 | const char* args_create_help_string( args_context_t* ctx, char* buffer, size_t buffer_size ); 153 | 154 | #endif -------------------------------------------------------------------------------- /src/hb_ascii.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ASCII An implementation of hashbase commands. 3 | * 4 | * Version: @(#)ascii.c 0.0.1 09/07/14 5 | * Authors: Maciej A. Czyzewski, 6 | * 7 | */ 8 | 9 | #include 10 | 11 | extern map_t database; 12 | 13 | pipe_t ascii_inf(pipe_t *tokens) 14 | { 15 | pipe_t buffer = pipe_empty(); 16 | 17 | buffer = pipe_new("hashbase "); 18 | buffer = pipe_cat(buffer, HB_VERSION); 19 | buffer = pipe_cat(buffer, " (c) 2014 Maciej A. Czyzewski"); 20 | 21 | return buffer; 22 | } 23 | 24 | pipe_t ascii_set(pipe_t *tokens) 25 | { 26 | pipe_t buffer = pipe_empty(); 27 | 28 | map_put(&database, tokens[1], tokens[2]); 29 | buffer = pipe_fromlonglong(HB_OK); 30 | 31 | return buffer; 32 | } 33 | 34 | pipe_t ascii_get(pipe_t *tokens) 35 | { 36 | pipe_t buffer = pipe_empty(); 37 | 38 | if ((map_get(&database, tokens[1], (void**)(&buffer))) != HB_ERR) { 39 | buffer = pipe_new(buffer); 40 | }else{ 41 | buffer = pipe_fromlonglong(HB_ERR); 42 | } 43 | 44 | return buffer; 45 | } 46 | 47 | pipe_t ascii_del(pipe_t *tokens) 48 | { 49 | pipe_t buffer = pipe_empty(); 50 | 51 | map_remove(&database, tokens[1]); 52 | buffer = pipe_fromlonglong(HB_OK); 53 | 54 | return buffer; 55 | } 56 | 57 | pipe_t ascii_len(pipe_t *tokens) 58 | { 59 | pipe_t buffer = pipe_empty(); 60 | 61 | buffer = pipe_fromlonglong(map_length(&database)); 62 | 63 | return buffer; 64 | } 65 | 66 | pipe_t ascii_clr(pipe_t *tokens) 67 | { 68 | pipe_t buffer = pipe_empty(); 69 | 70 | map_free(&database); 71 | buffer = pipe_fromlonglong(HB_OK); 72 | 73 | return buffer; 74 | } -------------------------------------------------------------------------------- /src/hb_ascii.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hashbase - https://github.com/MaciejCzyzewski/hashbase 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2014 Maciej A. Czyzewski 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Author: Maciej A. Czyzewski 26 | */ 27 | 28 | #ifndef _HB_ASCII_H_ 29 | #define _HB_ASCII_H_ 30 | 31 | struct ascii_t { 32 | const char *name; 33 | pipe_t (*func)(pipe_t *); 34 | }; 35 | 36 | pipe_t ascii_inf(pipe_t *); 37 | pipe_t ascii_set(pipe_t *); 38 | pipe_t ascii_get(pipe_t *); 39 | pipe_t ascii_del(pipe_t *); 40 | pipe_t ascii_len(pipe_t *); 41 | pipe_t ascii_clr(pipe_t *); 42 | 43 | #endif -------------------------------------------------------------------------------- /src/hb_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CORE --- 3 | * 4 | * Version: @(#)core.c 0.0.1 09/07/14 5 | * Authors: Maciej A. Czyzewski, 6 | * 7 | * Fixes: 8 | * M. A. Czyzewski : Some small cleanups, optimizations, and fixed a 9 | * core_close() bug. 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | extern struct server server; 22 | extern struct client client; 23 | 24 | static void do_daemonize(); 25 | static void do_stop(); 26 | 27 | static void print_help(args_context_t); 28 | static void print_version(args_context_t); 29 | 30 | static void do_daemonize() 31 | { 32 | server.daemonize = true; 33 | server.pid = fork(); 34 | 35 | if (server.pid < 0) { 36 | fprintf(stdout, "hb: %s fork failed\n", HB_LOG_ERR); 37 | core_close(1); 38 | } 39 | 40 | if (server.pid > 0) { 41 | FILE *f = fopen(server.lock, "w+"); 42 | 43 | if (f != NULL) { 44 | fprintf(f, "%d", server.pid); 45 | fclose(f); 46 | } 47 | 48 | fprintf(stdout, "hb: %s daemon process running [fd: %d]...\n", HB_LOG_OK, server.pid); 49 | exit(0); 50 | } 51 | 52 | umask(0); 53 | 54 | if ((server.pid = setsid()) < 0) { 55 | core_close(1); 56 | } 57 | 58 | server.status = chdir("/"); 59 | 60 | close(STDIN_FILENO); 61 | close(STDOUT_FILENO); 62 | close(STDERR_FILENO); 63 | } 64 | 65 | static void do_stop() 66 | { 67 | pid_t pid; 68 | 69 | FILE *f = fopen(server.lock, "r"); 70 | 71 | if (f != NULL) { 72 | fscanf(f, "%d", &pid); 73 | fclose(f); 74 | 75 | kill(pid, SIGKILL); 76 | kill(pid, SIGTERM); 77 | 78 | remove(server.lock); 79 | } 80 | 81 | core_close(2); 82 | } 83 | 84 | static void print_help(args_context_t ctx) 85 | { 86 | char buffer[2048]; 87 | printf("\nUsage: hashbase [options]\n\n"); 88 | printf("%s\n", args_create_help_string(&ctx, buffer, sizeof(buffer))); 89 | core_close(0); 90 | } 91 | 92 | static void print_version(args_context_t ctx) 93 | { 94 | printf("%s\n", HB_VERSION); 95 | core_close(0); 96 | } 97 | 98 | static const args_option_t option_list[] = { 99 | { "daemonize", 'd', ARGS_OPTION_TYPE_NO_ARG, 0x0, 'd', "run hashbase as a daemon", 0x0 }, 100 | { "stop", 's', ARGS_OPTION_TYPE_NO_ARG, 0x0, 's', "close running daemon", 0x0 }, 101 | { "port", 'p', ARGS_OPTION_TYPE_REQUIRED, 0x0, 'p', "set the tcp port to listen on", "NUMBER" }, 102 | { "help", 'h', ARGS_OPTION_TYPE_NO_ARG, 0x0, 'h', "show hashbase version, usage, options, and exit", 0x0 }, 103 | { "version", 'v', ARGS_OPTION_TYPE_NO_ARG, 0x0, 'v', "show version and exit", 0x0 }, 104 | ARGS_OPTIONS_END 105 | }; 106 | 107 | void core_init(int argc, char *argv[]) 108 | { 109 | args_context_t ctx; 110 | args_create_context(&ctx, argc, argv, option_list); 111 | 112 | int opt; 113 | 114 | while ( ( opt = args_next( &ctx ) ) != -1 ) { 115 | switch ( opt ) { 116 | /* Warnings */ 117 | case '+': 118 | fprintf(stdout, "hb: %s got argument without flag [%s]\n", HB_LOG_WRN, ctx.current_opt_arg); 119 | break; 120 | case '?': 121 | fprintf(stdout, "hb: %s unknown flag [%s]\n", HB_LOG_WRN, ctx.current_opt_arg); 122 | break; 123 | case '!': 124 | fprintf(stdout, "hb: %s invalid use of flag [%s]\n", HB_LOG_WRN, ctx.current_opt_arg); 125 | break; 126 | /* Options */ 127 | case 'd': 128 | do_daemonize(); 129 | break; 130 | case 's': 131 | do_stop(); 132 | break; 133 | case 'p': 134 | server.port = atoi(ctx.current_opt_arg); 135 | break; 136 | /* Help & Version */ 137 | case 'h': 138 | print_help(ctx); 139 | break; 140 | case 'v': 141 | print_version(ctx); 142 | break; 143 | default: 144 | break; 145 | } 146 | } 147 | } 148 | 149 | void core_close(int code) 150 | { 151 | server.keepRunning = false; 152 | 153 | close(client.socket); 154 | close(server.socket); 155 | 156 | switch (code) { 157 | /* Signal close */ 158 | case 2: 159 | fprintf(stdout, "hb: %s closing hashbase...\n", HB_LOG_INF); 160 | exit(0); 161 | break; 162 | /* Error */ 163 | case 1: 164 | exit(1); 165 | break; 166 | /* Default */ 167 | default: 168 | exit(0); 169 | break; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/hb_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hashbase - https://github.com/MaciejCzyzewski/hashbase 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2014 Maciej A. Czyzewski 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Author: Maciej A. Czyzewski 26 | */ 27 | 28 | #ifndef _HB_CORE_H_ 29 | #define _HB_CORE_H_ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | /*----------------------------------------------------------------------------- 56 | * HASHBASE config/macros 57 | *-------------------------------------------------------------------------- */ 58 | 59 | #define HB_VERSION "0.0.1" 60 | 61 | #define HB_OK 0 62 | #define HB_ERR -1 63 | 64 | #define HB_LOG_OK "\033[1;32m ok >>\033[0m" 65 | #define HB_LOG_ERR "\033[1;31merror >>\033[0m" 66 | #define HB_LOG_WRN "\033[1;33m warn >>\033[0m" 67 | #define HB_LOG_INF " info >>" 68 | 69 | #define HB_MAP_SIZE 512 70 | #define HB_MAP_LENGTH 256 71 | 72 | #define HB_NET_PORT 5555 73 | #define HB_NET_BUFFER 512 74 | #define HB_NET_BACKLOG 256 75 | 76 | #define HB_CORE_LOCK "/tmp/hashbase.pid" 77 | #define HB_CORE_MAX_OPTIONS 32 78 | #define HB_CORE_MAX_ARGS 32 79 | 80 | #define HB_PIPE_PREALLOC (1024*1024) 81 | 82 | /*----------------------------------------------------------------------------- 83 | * HASHBASE modules 84 | *-------------------------------------------------------------------------- */ 85 | 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | 93 | /*----------------------------------------------------------------------------- 94 | * HASHBASE server/client 95 | *-------------------------------------------------------------------------- */ 96 | 97 | struct server { 98 | /* options with no argument */ 99 | 100 | int status; /* memory : last status code */ 101 | 102 | struct ascii_t * commands; /* ascii : commands map */ 103 | 104 | int buffer; /* network : packet lenght */ 105 | int backlog; /* network : tcp backlog */ 106 | int port; /* network : tcp listening port */ 107 | int socket; /* network : tcp socket */ 108 | struct sockaddr_in addr; /* network : tcp addr */ 109 | 110 | pid_t pid; /* process : pid */ 111 | char * lock; /* process : lock */ 112 | bool daemonize:1; /* process : daemon */ 113 | bool keepRunning:1; /* process : status */ 114 | }; 115 | 116 | struct client { 117 | /* options with no argument */ 118 | 119 | int socket; /* network : tcp socket */ 120 | struct sockaddr_in addr; /* network : tcp addr */ 121 | int size; /* network : tcp size */ 122 | }; 123 | 124 | /*----------------------------------------------------------------------------- 125 | * HASHBASE functions 126 | *-------------------------------------------------------------------------- */ 127 | 128 | void core_init(int, char * []); 129 | void core_close(int); 130 | 131 | #endif -------------------------------------------------------------------------------- /src/hb_map.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MAP Generic hash map/table manipulation functions. 3 | * 4 | * Version: @(#)map.c 0.0.1 09/07/14 5 | * Authors: Maciej A. Czyzewski, 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | extern map_t database; 16 | 17 | static unsigned long crc32(const unsigned char *, unsigned int); 18 | static unsigned int map_hash_int(map_t *, char *); 19 | static int map_hash(map_t *, char *); 20 | static int map_rehash(map_t *); 21 | 22 | int map_init(void) 23 | { 24 | database = * map_new(); 25 | 26 | return HB_OK; 27 | } 28 | 29 | map_t *map_new() 30 | { 31 | map_t* m = (map_t*) malloc(sizeof(map_t)); 32 | if(!m) goto err; 33 | 34 | m->data = (map_bucket_t*) calloc(HB_MAP_SIZE, sizeof(map_bucket_t)); 35 | if(!m->data) goto err; 36 | 37 | m->table_size = HB_MAP_SIZE; 38 | m->size = 0; 39 | 40 | return m; 41 | err: 42 | if (m) 43 | map_free(m); 44 | return NULL; 45 | } 46 | 47 | /* The implementation here was originally done by Gary S. Brown. I have 48 | * borrowed the tables directly, and made some minor changes to the 49 | * crc32-function (including changing the interface). //ylo */ 50 | 51 | /* ============================================================= */ 52 | /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ 53 | /* code or tables extracted from it, as desired without restriction. */ 54 | /* */ 55 | /* First, the polynomial itself and its table of feedback terms. The */ 56 | /* polynomial is */ 57 | /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ 58 | /* */ 59 | /* Note that we take it "backwards" and put the highest-order term in */ 60 | /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ 61 | /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ 62 | /* the MSB being 1. */ 63 | /* */ 64 | /* Note that the usual hardware shift register implementation, which */ 65 | /* is what we're using (we're merely optimizing it by doing eight-bit */ 66 | /* chunks at a time) shifts bits into the lowest-order term. In our */ 67 | /* implementation, that means shifting towards the right. Why do we */ 68 | /* do it this way? Because the calculated CRC must be transmitted in */ 69 | /* order from highest-order term to lowest-order term. UARTs transmit */ 70 | /* characters in order from LSB to MSB. By storing the CRC this way, */ 71 | /* we hand it to the UART in the order low-byte to high-byte; the UART */ 72 | /* sends each low-bit to hight-bit; and the result is transmission bit */ 73 | /* by bit from highest- to lowest-order term without requiring any bit */ 74 | /* shuffling on our part. Reception works similarly. */ 75 | /* */ 76 | /* The feedback terms table consists of 256, 32-bit entries. Notes: */ 77 | /* */ 78 | /* The table can be generated at runtime if desired; code to do so */ 79 | /* is shown later. It might not be obvious, but the feedback */ 80 | /* terms simply represent the results of eight shift/xor opera- */ 81 | /* tions for all combinations of data and CRC register values. */ 82 | /* */ 83 | /* The values must be right-shifted by eight bits by the "updcrc" */ 84 | /* logic; the shift must be unsigned (bring in zeroes). On some */ 85 | /* hardware you could probably optimize the shift in assembler by */ 86 | /* using byte-swap instructions. */ 87 | /* polynomial $edb88320 */ 88 | /* */ 89 | /* -------------------------------------------------------------------- */ 90 | 91 | static unsigned long crc32_tab[] = { 92 | 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 93 | 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 94 | 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 95 | 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 96 | 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 97 | 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 98 | 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 99 | 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 100 | 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 101 | 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 102 | 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 103 | 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 104 | 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 105 | 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 106 | 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 107 | 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 108 | 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 109 | 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 110 | 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 111 | 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 112 | 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 113 | 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 114 | 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 115 | 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 116 | 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 117 | 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 118 | 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 119 | 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 120 | 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 121 | 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 122 | 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 123 | 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 124 | 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 125 | 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 126 | 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 127 | 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 128 | 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 129 | 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 130 | 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 131 | 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 132 | 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 133 | 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 134 | 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 135 | 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 136 | 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 137 | 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 138 | 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 139 | 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 140 | 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 141 | 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 142 | 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 143 | 0x2d02ef8dL 144 | }; 145 | 146 | /* Return a 32-bit CRC of the contents of the buffer. */ 147 | static unsigned long crc32(const unsigned char *s, unsigned int len) 148 | { 149 | unsigned int i; 150 | unsigned long crc32val; 151 | 152 | crc32val = 0; 153 | for (i = 0; i < len; i ++) { 154 | crc32val = 155 | crc32_tab[(crc32val ^ s[i]) & 0xff] ^ 156 | (crc32val >> 8); 157 | } 158 | return crc32val; 159 | } 160 | 161 | static unsigned int map_hash_int(map_t * m, char* keystring) 162 | { 163 | /* CRC32 initial key */ 164 | unsigned long key = crc32((unsigned char*)(keystring), strlen(keystring)); 165 | 166 | /* Robert Jenkins' 32 bit Mix Function */ 167 | key += (key << 12); 168 | key ^= (key >> 22); 169 | key += (key << 4); 170 | key ^= (key >> 9); 171 | key += (key << 10); 172 | key ^= (key >> 2); 173 | key += (key << 7); 174 | key ^= (key >> 12); 175 | 176 | /* Knuth's Multiplicative Method */ 177 | key = (key >> 3) * 2654435761; 178 | 179 | return key % m->table_size; 180 | } 181 | 182 | /* Return the integer of the location in data 183 | * to store the point to the item, or HB_MAP_FULL. */ 184 | static int map_hash(map_t * m, char* key) 185 | { 186 | int curr; 187 | int i; 188 | 189 | /* If full, return immediately */ 190 | if(m->size >= (m->table_size/2)) return HB_MAP_FULL; 191 | 192 | /* Find the best index */ 193 | curr = map_hash_int(m, key); 194 | 195 | /* Linear probing */ 196 | for(i = 0; idata[curr].in_use == 0) 198 | return curr; 199 | 200 | if(m->data[curr].in_use == 1 && (strcmp(m->data[curr].key,key)==0)) 201 | return curr; 202 | 203 | curr = (curr + 1) % m->table_size; 204 | } 205 | 206 | return HB_MAP_FULL; 207 | } 208 | 209 | /* Doubles the size of the map, and rehashes all the elements */ 210 | static int map_rehash(map_t * m) 211 | { 212 | int i; 213 | int old_size; 214 | map_bucket_t* curr; 215 | 216 | /* Setup the new elements */ 217 | map_bucket_t* temp = (map_bucket_t *) 218 | calloc(2 * m->table_size, sizeof(map_bucket_t)); 219 | if(!temp) return HB_MAP_OMEM; 220 | 221 | /* Update the array */ 222 | curr = m->data; 223 | m->data = temp; 224 | 225 | /* Update the size */ 226 | old_size = m->table_size; 227 | m->table_size = 2 * m->table_size; 228 | m->size = 0; 229 | 230 | /* Rehash the elements */ 231 | for(i = 0; i < old_size; i++) { 232 | int status; 233 | 234 | if (curr[i].in_use == 0) 235 | continue; 236 | 237 | status = map_put(m, curr[i].key, curr[i].data); 238 | if (status != HB_OK) 239 | return status; 240 | } 241 | 242 | free(curr); 243 | 244 | return HB_OK; 245 | } 246 | 247 | /* Add a pointer to the map with some key */ 248 | int map_put(map_t * m, char* key, any_t value) 249 | { 250 | int index; 251 | 252 | /* Find a place to put our value */ 253 | index = map_hash(m, key); 254 | while(index == HB_MAP_FULL) { 255 | if (map_rehash(m) == HB_MAP_OMEM) { 256 | return HB_MAP_OMEM; 257 | } 258 | index = map_hash(m, key); 259 | } 260 | 261 | /* Set the data */ 262 | m->data[index].data = value; 263 | m->data[index].key = key; 264 | m->data[index].in_use = 1; 265 | m->size++; 266 | 267 | return HB_OK; 268 | } 269 | 270 | /* Get your pointer out of the map with a key */ 271 | int map_get(map_t * m, char* key, any_t *arg) 272 | { 273 | int curr; 274 | int i; 275 | 276 | /* Find data location */ 277 | curr = map_hash_int(m, key); 278 | 279 | /* Linear probing, if necessary */ 280 | for(i = 0; idata[curr].in_use; 283 | if (in_use == 1) { 284 | if (strcmp(m->data[curr].key,key)==0) { 285 | *arg = (m->data[curr].data); 286 | return HB_OK; 287 | } 288 | } 289 | 290 | curr = (curr + 1) % m->table_size; 291 | } 292 | 293 | *arg = NULL; 294 | 295 | /* Not found */ 296 | return HB_ERR; 297 | } 298 | 299 | /* Iterate the function parameter over each element in the map. The 300 | * additional any_t argument is passed to the function as its first 301 | * argument and the map element is the second. */ 302 | int map_iterate(map_t * m, PFany f, any_t item) 303 | { 304 | int i; 305 | 306 | /* On empty map, return immediately */ 307 | if (map_length(m) <= 0) 308 | return HB_ERR; 309 | 310 | /* Linear probing */ 311 | for(i = 0; i< m->table_size; i++) 312 | if(m->data[i].in_use != 0) { 313 | any_t data = (any_t) (m->data[i].data); 314 | int status = f(item, data); 315 | if (status != HB_OK) { 316 | return status; 317 | } 318 | } 319 | 320 | return HB_OK; 321 | } 322 | 323 | /* Remove an element with that key from the map */ 324 | int map_remove(map_t * m, char* key) 325 | { 326 | int i; 327 | int curr; 328 | 329 | /* Find key */ 330 | curr = map_hash_int(m, key); 331 | 332 | /* Linear probing, if necessary */ 333 | for(i = 0; idata[curr].in_use; 336 | if (in_use == 1) { 337 | if (strcmp(m->data[curr].key,key)==0) { 338 | /* Blank out the fields */ 339 | m->data[curr].in_use = 0; 340 | m->data[curr].data = NULL; 341 | m->data[curr].key = NULL; 342 | 343 | /* Reduce the size */ 344 | m->size--; 345 | return HB_OK; 346 | } 347 | } 348 | curr = (curr + 1) % m->table_size; 349 | } 350 | 351 | /* Data not found */ 352 | return HB_ERR; 353 | } 354 | 355 | /* Deallocate the map */ 356 | void map_free(map_t * m) 357 | { 358 | free(m->data); 359 | free(m); 360 | } 361 | 362 | /* Return the length of the map */ 363 | int map_length(map_t * m) 364 | { 365 | if(m != NULL) return m->size; 366 | else return 0; 367 | } -------------------------------------------------------------------------------- /src/hb_map.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hashbase - https://github.com/MaciejCzyzewski/hashbase 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2014 Maciej A. Czyzewski 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Author: Maciej A. Czyzewski 26 | */ 27 | 28 | #ifndef _HB_MAP_H_ 29 | #define _HB_MAP_H_ 30 | 31 | #define HB_MAP_FULL -3 /* Hashmap is full */ 32 | #define HB_MAP_OMEM -2 /* Out of Memory */ 33 | 34 | /* any_t is a pointer. This allows you to put arbitrary structures in 35 | * the map. */ 36 | typedef void *any_t; 37 | 38 | /* PFany is a pointer to a function that can take two any_t arguments 39 | * and return an integer. Returns status code.. */ 40 | typedef int (*PFany)(any_t, any_t); 41 | 42 | /* We need to keep keys and values. */ 43 | typedef struct _map_bucket { 44 | char* key; 45 | int in_use; 46 | any_t data; 47 | } map_bucket_t; 48 | 49 | /* A map has some maximum size and current size, 50 | * as well as the data to hold. */ 51 | typedef struct _map { 52 | int table_size; 53 | int size; 54 | map_bucket_t *data; 55 | } map_t; 56 | 57 | /* Create global database. */ 58 | int map_init(void); 59 | 60 | /* Return an empty map. Returns NULL if empty. */ 61 | map_t *map_new(void); 62 | 63 | /* Iteratively call f with argument (item, data) for 64 | * each element data in the map. The function must 65 | * return a map status code. If it returns anything other 66 | * than HB_OK the traversal is terminated. f must 67 | * not reenter any map functions, or deadlock may arise. */ 68 | int map_iterate(map_t *, PFany, any_t); 69 | 70 | /* Add an element to the map. Return HB_OK or MAP_OMEM. */ 71 | int map_put(map_t *, char *, any_t); 72 | 73 | /* Get an element from the map. Return HB_OK or HB_ERR. */ 74 | int map_get(map_t *, char *, any_t *); 75 | 76 | /* Remove an element from the map. Return HB_OK or HB_ERR. */ 77 | int map_remove(map_t *, char *); 78 | 79 | /* Get any element. Return HB_OK or HB_ERR. 80 | * remove - should the element be removed from the map */ 81 | int map_get_one(map_t *, any_t *, int); 82 | 83 | /* Free the map. */ 84 | void map_free(map_t *); 85 | 86 | /* Get the current size of a map. */ 87 | int map_length(map_t *); 88 | 89 | #endif -------------------------------------------------------------------------------- /src/hb_net.c: -------------------------------------------------------------------------------- 1 | /* 2 | * NET An implementation of the TCP/UDP network access protocol. 3 | * 4 | * Version: @(#)net.c 0.0.1 09/07/14 5 | * Authors: Maciej A. Czyzewski, 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | extern struct server server; 16 | extern struct client client; 17 | 18 | int net_init(void) 19 | { 20 | if ((server.socket = socket(AF_INET, SOCK_STREAM, 0)) == HB_ERR) { 21 | fprintf(stdout, "hb: %s could not create socket\n", HB_LOG_ERR); 22 | return HB_ERR; 23 | } 24 | 25 | server.addr.sin_family = AF_INET; 26 | server.addr.sin_addr.s_addr = INADDR_ANY; 27 | server.addr.sin_port = htons(server.port); 28 | 29 | if (bind(server.socket, (struct sockaddr *)&server.addr, sizeof(server.addr)) < HB_OK) { 30 | fprintf(stdout, "hb: %s port is already in use\n", HB_LOG_ERR); 31 | return HB_ERR; 32 | } 33 | 34 | listen(server.socket, server.backlog); 35 | 36 | return HB_OK; 37 | } 38 | 39 | int net_loop(void) 40 | { 41 | pthread_t thread_id; 42 | 43 | while ((client.socket = accept(server.socket, (struct sockaddr *)&client.addr, (socklen_t*)&client.size)) || server.keepRunning) { 44 | if (client.socket < HB_OK) { 45 | fprintf(stdout, "hb: %s connection failed\n", HB_LOG_ERR); 46 | return HB_ERR; 47 | } 48 | 49 | fprintf(stdout, "hb: %s connection accepted [fd: %d]\n", HB_LOG_OK, client.socket); 50 | 51 | if (pthread_create(&thread_id, NULL, net_handler, (void*) &client.socket) < HB_OK) { 52 | fprintf(stdout, "hb: %s could not create thread\n", HB_LOG_ERR); 53 | return HB_ERR; 54 | } 55 | } 56 | 57 | return HB_OK; 58 | } 59 | 60 | void *net_handler(void *socket_desc) 61 | { 62 | int sock = *(int*)socket_desc; 63 | int read_size = 0; 64 | 65 | char assocc[HB_NET_BUFFER]; 66 | pipe_t buffer = pipe_empty(); 67 | 68 | while ((read_size = recv(sock, assocc, HB_NET_BUFFER, 0)) > 0) { 69 | pipe_t packet = pipe_newlen(assocc, read_size); 70 | 71 | buffer = pipe_catpipe(buffer, packet); 72 | packet = pipe_empty(); 73 | 74 | if (pipe_len(buffer) > 1) { 75 | if (buffer[(pipe_len(buffer)-1)] == '\n' && buffer[(pipe_len(buffer)-2)] == '\r') { 76 | pipe_trim(buffer, "\r\n"); 77 | packet = net_command(buffer); 78 | packet = pipe_cat(packet, "\r\n"); 79 | server.status = write(sock, packet, (int) pipe_len(packet)); 80 | 81 | buffer = pipe_empty(); 82 | packet = pipe_empty(); 83 | } 84 | } 85 | 86 | pipe_free(packet); 87 | } 88 | 89 | switch (read_size) { 90 | case HB_OK: 91 | fprintf(stdout, "hb: %s client disconnected [fd: %d]\n", HB_LOG_OK, sock); 92 | fflush(stdout); 93 | break; 94 | case HB_ERR: 95 | fprintf(stdout, "hb: %s client receive failed [fd: %d]\n", HB_LOG_ERR, sock); 96 | break; 97 | } 98 | 99 | return HB_OK; 100 | } 101 | 102 | void *net_command(void *buffer) 103 | { 104 | pipe_t *tokens; 105 | int count, i; 106 | 107 | tokens = pipe_splitargs(buffer, &count); 108 | 109 | for (i = 0 ;; i++) { 110 | if ( server.commands[i].name == NULL ) { 111 | break; 112 | } else if (!strcmp(server.commands[i].name, tokens[0]) && server.commands[i].func) { 113 | return buffer = server.commands[i].func(tokens); 114 | } 115 | } 116 | 117 | return buffer = pipe_fromlonglong(HB_ERR); 118 | } 119 | -------------------------------------------------------------------------------- /src/hb_net.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hashbase - https://github.com/MaciejCzyzewski/hashbase 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2014 Maciej A. Czyzewski 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Author: Maciej A. Czyzewski 26 | */ 27 | 28 | #ifndef _HB_NET_H_ 29 | #define _HB_NET_H_ 30 | 31 | int net_init(void); 32 | int net_loop(void); 33 | void *net_handler(void *); 34 | void *net_command(void *); 35 | 36 | #endif -------------------------------------------------------------------------------- /src/hb_pipe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PIPE A simple thread-safe FIFO 3 | * 4 | * Version: @(#)pipe.c 0.0.1 09/07/14 5 | * Authors: Clark Gaebel, 6 | * Salvatore Sanfilippo 7 | * Maciej A. Czyzewski, 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | /* Create a new pipe_t string with the content specified by the 'init' pointer 20 | * and 'initlen'. 21 | * If NULL is used for 'init' the string is initialized with zero bytes. 22 | * 23 | * The string is always null-termined (all the pipe_t strings are, always) so 24 | * even if you create an pipe_t string with: 25 | * 26 | * mystring = pipe_newlen("abc",3"); 27 | * 28 | * You can print the string with printf() as there is an implicit \0 at the 29 | * end of the string. However the string is binary safe and can contain 30 | * \0 characters in the middle, as the length is stored in the pipe_t header. */ 31 | pipe_t pipe_newlen(const void *init, size_t initlen) 32 | { 33 | struct pipe_hdr_t *sh; 34 | 35 | if (init) { 36 | sh = malloc(sizeof *sh+initlen+1); 37 | } else { 38 | sh = calloc(sizeof *sh+initlen+1,1); 39 | } 40 | if (sh == NULL) return NULL; 41 | sh->len = initlen; 42 | sh->free = 0; 43 | if (initlen && init) 44 | memcpy(sh->buf, init, initlen); 45 | sh->buf[initlen] = '\0'; 46 | return (char*)sh->buf; 47 | } 48 | 49 | /* Create an empty (zero length) pipe_t string. Even in this case the string 50 | * always has an implicit null term. */ 51 | pipe_t pipe_empty(void) 52 | { 53 | return pipe_newlen("",0); 54 | } 55 | 56 | /* Create a new pipe_t string starting from a null termined C string. */ 57 | pipe_t pipe_new(const char *init) 58 | { 59 | size_t initlen = (init == NULL) ? 0 : strlen(init); 60 | return pipe_newlen(init, initlen); 61 | } 62 | 63 | size_t pipe_len(const pipe_t s) 64 | { 65 | struct pipe_hdr_t *sh = (void*)(s-sizeof *sh); 66 | return sh->len; 67 | } 68 | 69 | /* Duplicate an pipe_t string. */ 70 | pipe_t pipe_dup(const pipe_t s) 71 | { 72 | return pipe_newlen(s, pipe_len(s)); 73 | } 74 | 75 | /* Free an pipe_t string. No operation is performed if 's' is NULL. */ 76 | void pipe_free(pipe_t s) 77 | { 78 | if (s == NULL) return; 79 | free(s-sizeof(struct pipe_hdr_t)); 80 | } 81 | 82 | size_t pipe_avail(const pipe_t s) 83 | { 84 | struct pipe_hdr_t *sh = (void*)(s-sizeof *sh); 85 | return sh->free; 86 | } 87 | 88 | /* Set the pipe_t string length to the length as obtained with strlen(), so 89 | * considering as content only up to the first null term character. 90 | * 91 | * This function is useful when the pipe_t string is hacked manually in some 92 | * way, like in the following example: 93 | * 94 | * s = pipe_new("foobar"); 95 | * s[2] = '\0'; 96 | * pipe_updatelen(s); 97 | * printf("%d\n", pipe_len(s)); 98 | * 99 | * The output will be "2", but if we comment out the call to pipe_updatelen() 100 | * the output will be "6" as the string was modified but the logical length 101 | * remains 6 bytes. */ 102 | void pipe_updatelen(pipe_t s) 103 | { 104 | struct pipe_hdr_t *sh = (void*) (s-sizeof *sh);; 105 | int reallen = strlen(s); 106 | sh->free += (sh->len-reallen); 107 | sh->len = reallen; 108 | } 109 | 110 | /* Modify an pipe_t string on-place to make it empty (zero length). 111 | * However all the existing buffer is not discarded but set as free space 112 | * so that next append operations will not require allocations up to the 113 | * number of bytes previously available. */ 114 | void pipe_clear(pipe_t s) 115 | { 116 | struct pipe_hdr_t *sh = (void*) (s-sizeof *sh);; 117 | sh->free += sh->len; 118 | sh->len = 0; 119 | sh->buf[0] = '\0'; 120 | } 121 | 122 | /* Enlarge the free space at the end of the pipe_t string so that the caller 123 | * is sure that after calling this function can overwrite up to addlen 124 | * bytes after the end of the string, plus one more byte for nul term. 125 | * 126 | * Note: this does not change the *length* of the pipe_t string as returned 127 | * by pipelen(), but only the free buffer space we have. */ 128 | pipe_t pipe_MakeRoomFor(pipe_t s, size_t addlen) 129 | { 130 | struct pipe_hdr_t *sh, *newsh; 131 | size_t free = pipe_avail(s); 132 | size_t len, newlen; 133 | 134 | if (free >= addlen) return s; 135 | len = pipe_len(s); 136 | sh = (void*) (s-sizeof *sh);; 137 | newlen = (len+addlen); 138 | if (newlen < HB_PIPE_PREALLOC) 139 | newlen *= 2; 140 | else 141 | newlen += HB_PIPE_PREALLOC; 142 | newsh = realloc(sh, sizeof *newsh+newlen+1); 143 | if (newsh == NULL) return NULL; 144 | 145 | newsh->free = newlen - len; 146 | return newsh->buf; 147 | } 148 | 149 | /* Reallocate the pipe_t string so that it has no free space at the end. The 150 | * contained string remains not altered, but next concatenation operations 151 | * will require a reallocation. 152 | * 153 | * After the call, the passed pipe_t string is no longer valid and all the 154 | * references must be substituted with the new pointer returned by the call. */ 155 | pipe_t pipe_RemoveFreeSpace(pipe_t s) 156 | { 157 | struct pipe_hdr_t *sh; 158 | 159 | sh = (void*) (s-sizeof *sh);; 160 | sh = realloc(sh, sizeof *sh+sh->len+1); 161 | sh->free = 0; 162 | return sh->buf; 163 | } 164 | 165 | /* Return the total size of the allocation of the specifed pipe_t string, 166 | * including: 167 | * 1) The pipe_t header before the pointer. 168 | * 2) The string. 169 | * 3) The free buffer at the end if any. 170 | * 4) The implicit null term. 171 | */ 172 | size_t pipe_AllocSize(pipe_t s) 173 | { 174 | struct pipe_hdr_t *sh = (void*) (s-sizeof *sh);; 175 | 176 | return sizeof(*sh)+sh->len+sh->free+1; 177 | } 178 | 179 | /* Increment the pipe_t length and decrements the left free space at the 180 | * end of the string according to 'incr'. Also set the null term 181 | * in the new end of the string. 182 | * 183 | * This function is used in order to fix the string length after the 184 | * user calls pipe_MakeRoomFor(), writes something after the end of 185 | * the current string, and finally needs to set the new length. 186 | * 187 | * Note: it is possible to use a negative increment in order to 188 | * right-trim the string. 189 | * 190 | * Usage example: 191 | * 192 | * Using pipe_IncrLen() and pipe_MakeRoomFor() it is possible to mount the 193 | * following schema, to cat bytes coming from the kernel to the end of an 194 | * pipe_t string without copying into an intermediate buffer: 195 | * 196 | * oldlen = pipe_len(s); 197 | * s = pipe_MakeRoomFor(s, BUFFER_SIZE); 198 | * nread = read(fd, s+oldlen, BUFFER_SIZE); 199 | * ... check for nread <= 0 and handle it ... 200 | * pipe_IncrLen(s, nread); 201 | */ 202 | void pipe_IncrLen(pipe_t s, int incr) 203 | { 204 | struct pipe_hdr_t *sh = (void*) (s-sizeof *sh);; 205 | 206 | assert(sh->free >= incr); 207 | sh->len += incr; 208 | sh->free -= incr; 209 | assert(sh->free >= 0); 210 | s[sh->len] = '\0'; 211 | } 212 | 213 | /* Grow the pipe_t to have the specified length. Bytes that were not part of 214 | * the original length of the pipe_t will be set to zero. 215 | * 216 | * if the specified length is smaller than the current length, no operation 217 | * is performed. */ 218 | pipe_t pipe_growzero(pipe_t s, size_t len) 219 | { 220 | struct pipe_hdr_t *sh = (void*) (s-sizeof *sh); 221 | size_t totlen, curlen = sh->len; 222 | 223 | if (len <= curlen) return s; 224 | s = pipe_MakeRoomFor(s,len-curlen); 225 | if (s == NULL) return NULL; 226 | 227 | /* Make sure added region doesn't contain garbage */ 228 | sh = (void*)(s-sizeof *sh); 229 | memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ 230 | totlen = sh->len+sh->free; 231 | sh->len = len; 232 | sh->free = totlen-sh->len; 233 | return s; 234 | } 235 | 236 | /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the 237 | * end of the specified pipe_t string 's'. 238 | * 239 | * After the call, the passed pipe_t string is no longer valid and all the 240 | * references must be substituted with the new pointer returned by the call. */ 241 | pipe_t pipe_catlen(pipe_t s, const void *t, size_t len) 242 | { 243 | struct pipe_hdr_t *sh; 244 | size_t curlen = pipe_len(s); 245 | 246 | s = pipe_MakeRoomFor(s,len); 247 | if (s == NULL) return NULL; 248 | sh = (void*) (s-sizeof *sh);; 249 | memcpy(s+curlen, t, len); 250 | sh->len = curlen+len; 251 | sh->free = sh->free-len; 252 | s[curlen+len] = '\0'; 253 | return s; 254 | } 255 | 256 | /* Append the specified null termianted C string to the pipe_t string 's'. 257 | * 258 | * After the call, the passed pipe_t string is no longer valid and all the 259 | * references must be substituted with the new pointer returned by the call. */ 260 | pipe_t pipe_cat(pipe_t s, const char *t) 261 | { 262 | return pipe_catlen(s, t, strlen(t)); 263 | } 264 | 265 | /* Append the specified pipe_t 't' to the existing pipe_t 's'. 266 | * 267 | * After the call, the modified pipe_t string is no longer valid and all the 268 | * references must be substituted with the new pointer returned by the call. */ 269 | pipe_t pipe_catpipe(pipe_t s, const pipe_t t) 270 | { 271 | return pipe_catlen(s, t, pipe_len(t)); 272 | } 273 | 274 | /* Destructively modify the pipe_t string 's' to hold the specified binary 275 | * safe string pointed by 't' of length 'len' bytes. */ 276 | pipe_t pipe_cpylen(pipe_t s, const char *t, size_t len) 277 | { 278 | struct pipe_hdr_t *sh = (void*) (s-sizeof *sh);; 279 | size_t totlen = sh->free+sh->len; 280 | 281 | if (totlen < len) { 282 | s = pipe_MakeRoomFor(s,len-sh->len); 283 | if (s == NULL) return NULL; 284 | sh = (void*) (s-sizeof *sh);; 285 | totlen = sh->free+sh->len; 286 | } 287 | memcpy(s, t, len); 288 | s[len] = '\0'; 289 | sh->len = len; 290 | sh->free = totlen-len; 291 | return s; 292 | } 293 | 294 | /* Like pipe_cpylen() but 't' must be a null-termined string so that the length 295 | * of the string is obtained with strlen(). */ 296 | pipe_t pipe_cpy(pipe_t s, const char *t) 297 | { 298 | return pipe_cpylen(s, t, strlen(t)); 299 | } 300 | 301 | /* Like pipe_catpritf() but gets va_list instead of being variadic. */ 302 | pipe_t pipe_catvprintf(pipe_t s, const char *fmt, va_list ap) 303 | { 304 | va_list cpy; 305 | char *buf, *t; 306 | size_t buflen = 16; 307 | 308 | while(1) { 309 | buf = malloc(buflen); 310 | if (buf == NULL) return NULL; 311 | buf[buflen-2] = '\0'; 312 | va_copy(cpy,ap); 313 | vsnprintf(buf, buflen, fmt, cpy); 314 | if (buf[buflen-2] != '\0') { 315 | free(buf); 316 | buflen *= 2; 317 | continue; 318 | } 319 | break; 320 | } 321 | t = pipe_cat(s, buf); 322 | free(buf); 323 | return t; 324 | } 325 | 326 | /* Append to the pipe_t string 's' a string obtained using printf-alike format 327 | * specifier. 328 | * 329 | * After the call, the modified pipe_t string is no longer valid and all the 330 | * references must be substituted with the new pointer returned by the call. 331 | * 332 | * Example: 333 | * 334 | * s = pipe_empty("Sum is: "); 335 | * s = pipe_catprintf(s,"%d+%d = %d",a,b,a+b). 336 | * 337 | * Often you need to create a string from scratch with the printf-alike 338 | * format. When this is the need, just use pipe_empty() as the target string: 339 | * 340 | * s = pipe_catprintf(pipe_empty(), "... your format ...", args); 341 | */ 342 | pipe_t pipe_catprintf(pipe_t s, const char *fmt, ...) 343 | { 344 | va_list ap; 345 | char *t; 346 | va_start(ap, fmt); 347 | t = pipe_catvprintf(s,fmt,ap); 348 | va_end(ap); 349 | return t; 350 | } 351 | 352 | /* Remove the part of the string from left and from right composed just of 353 | * contiguous characters found in 'cset', that is a null terminted C string. 354 | * 355 | * After the call, the modified pipe_t string is no longer valid and all the 356 | * references must be substituted with the new pointer returned by the call. 357 | * 358 | * Example: 359 | * 360 | * s = pipe_new("AA...AA.a.aa.aHelloWorld :::"); 361 | * s = pipe_trim(s,"A. :"); 362 | * printf("%s\n", s); 363 | * 364 | * Output will be just "Hello World". 365 | */ 366 | void pipe_trim(pipe_t s, const char *cset) 367 | { 368 | struct pipe_hdr_t *sh = (void*) (s-sizeof *sh);; 369 | char *start, *end, *sp, *ep; 370 | size_t len; 371 | 372 | sp = start = s; 373 | ep = end = s+pipe_len(s)-1; 374 | while(sp <= end && strchr(cset, *sp)) sp++; 375 | while(ep > start && strchr(cset, *ep)) ep--; 376 | len = (sp > ep) ? 0 : ((ep-sp)+1); 377 | if (sh->buf != sp) memmove(sh->buf, sp, len); 378 | sh->buf[len] = '\0'; 379 | sh->free = sh->free+(sh->len-len); 380 | sh->len = len; 381 | } 382 | 383 | /* Turn the string into a smaller (or equal) string containing only the 384 | * substring specified by the 'start' and 'end' indexes. 385 | * 386 | * start and end can be negative, where -1 means the last character of the 387 | * string, -2 the penultimate character, and so forth. 388 | * 389 | * The interval is inclusive, so the start and end characters will be part 390 | * of the resulting string. 391 | * 392 | * The string is modified in-place. 393 | * 394 | * Example: 395 | * 396 | * s = pipe_new("Hello World"); 397 | * pipe_range(s,1,-1); => "ello World" 398 | */ 399 | void pipe_range(pipe_t s, int start, int end) 400 | { 401 | struct pipe_hdr_t *sh = (void*) (s-sizeof *sh);; 402 | size_t newlen, len = pipe_len(s); 403 | 404 | if (len == 0) return; 405 | if (start < 0) { 406 | start = len+start; 407 | if (start < 0) start = 0; 408 | } 409 | if (end < 0) { 410 | end = len+end; 411 | if (end < 0) end = 0; 412 | } 413 | newlen = (start > end) ? 0 : (end-start)+1; 414 | if (newlen != 0) { 415 | if (start >= (signed)len) { 416 | newlen = 0; 417 | } else if (end >= (signed)len) { 418 | end = len-1; 419 | newlen = (start > end) ? 0 : (end-start)+1; 420 | } 421 | } else { 422 | start = 0; 423 | } 424 | if (start && newlen) memmove(sh->buf, sh->buf+start, newlen); 425 | sh->buf[newlen] = 0; 426 | sh->free = sh->free+(sh->len-newlen); 427 | sh->len = newlen; 428 | } 429 | 430 | /* Apply tolower() to every character of the pipe_t string 's'. */ 431 | void pipe_tolower(pipe_t s) 432 | { 433 | int len = pipe_len(s), j; 434 | 435 | for (j = 0; j < len; j++) s[j] = tolower(s[j]); 436 | } 437 | 438 | /* Apply toupper() to every character of the pipe_t string 's'. */ 439 | void pipe_toupper(pipe_t s) 440 | { 441 | int len = pipe_len(s), j; 442 | 443 | for (j = 0; j < len; j++) s[j] = toupper(s[j]); 444 | } 445 | 446 | /* Compare two pipe_t strings s1 and s2 with memcmp(). 447 | * 448 | * Return value: 449 | * 450 | * 1 if s1 > s2. 451 | * -1 if s1 < s2. 452 | * 0 if s1 and s2 are exactly the same binary string. 453 | * 454 | * If two strings share exactly the same prefix, but one of the two has 455 | * additional characters, the longer string is considered to be greater than 456 | * the smaller one. */ 457 | int pipe_cmp(const pipe_t s1, const pipe_t s2) 458 | { 459 | size_t l1, l2, minlen; 460 | int cmp; 461 | 462 | l1 = pipe_len(s1); 463 | l2 = pipe_len(s2); 464 | minlen = (l1 < l2) ? l1 : l2; 465 | cmp = memcmp(s1,s2,minlen); 466 | if (cmp == 0) return l1-l2; 467 | return cmp; 468 | } 469 | 470 | /* Split 's' with separator in 'sep'. An array 471 | * of pipe_t strings is returned. *count will be set 472 | * by reference to the number of tokens returned. 473 | * 474 | * On out of memory, zero length string, zero length 475 | * separator, NULL is returned. 476 | * 477 | * Note that 'sep' is able to split a string using 478 | * a multi-character separator. For example 479 | * pipe_split("foo_-_bar","_-_"); will return two 480 | * elements "foo" and "bar". 481 | * 482 | * This version of the function is binary-safe but 483 | * requires length arguments. pipe_split() is just the 484 | * same function but for zero-terminated strings. 485 | */ 486 | pipe_t *pipe_splitlen(const char *s, int len, const char *sep, int seplen, int *count) 487 | { 488 | int elements = 0, slots = 5, start = 0, j; 489 | pipe_t *tokens; 490 | 491 | if (seplen < 1 || len < 0) return NULL; 492 | 493 | tokens = malloc(sizeof(pipe)*slots); 494 | if (tokens == NULL) return NULL; 495 | 496 | if (len == 0) { 497 | *count = 0; 498 | return tokens; 499 | } 500 | for (j = 0; j < (len-(seplen-1)); j++) { 501 | /* make sure there is room for the next element and the final one */ 502 | if (slots < elements+2) { 503 | pipe_t *newtokens; 504 | 505 | slots *= 2; 506 | newtokens = realloc(tokens,sizeof(pipe)*slots); 507 | if (newtokens == NULL) goto cleanup; 508 | tokens = newtokens; 509 | } 510 | /* search the separator */ 511 | if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { 512 | tokens[elements] = pipe_newlen(s+start,j-start); 513 | if (tokens[elements] == NULL) goto cleanup; 514 | elements++; 515 | start = j+seplen; 516 | j = j+seplen-1; /* skip the separator */ 517 | } 518 | } 519 | /* Add the final element. We are sure there is room in the tokens array. */ 520 | tokens[elements] = pipe_newlen(s+start,len-start); 521 | if (tokens[elements] == NULL) goto cleanup; 522 | elements++; 523 | *count = elements; 524 | return tokens; 525 | 526 | cleanup: { 527 | int i; 528 | for (i = 0; i < elements; i++) pipe_free(tokens[i]); 529 | free(tokens); 530 | *count = 0; 531 | return NULL; 532 | } 533 | } 534 | 535 | /* Free the result returned by pipe_splitlen(), or do nothing if 'tokens' is NULL. */ 536 | void pipe_freesplitres(pipe_t *tokens, int count) 537 | { 538 | if (!tokens) return; 539 | while(count--) 540 | pipe_free(tokens[count]); 541 | free(tokens); 542 | } 543 | 544 | /* Create an pipe_t string from a long long value. It is much faster than: 545 | * 546 | * pipe_catprintf(pipeempty(),"%lld\n", value); 547 | */ 548 | pipe_t pipe_fromlonglong(long long value) 549 | { 550 | char buf[32], *p; 551 | unsigned long long v; 552 | 553 | v = (value < 0) ? -value : value; 554 | p = buf+31; /* point to the last character */ 555 | do { 556 | *p-- = '0'+(v%10); 557 | v /= 10; 558 | } while(v); 559 | if (value < 0) *p-- = '-'; 560 | p++; 561 | return pipe_newlen(p,32-(p-buf)); 562 | } 563 | 564 | /* Append to the pipe_t string "s" an escaped string representation where 565 | * all the non-printable characters (tested with isprint()) are turned into 566 | * escapes in the form "\n\r\a...." or "\x". 567 | * 568 | * After the call, the modified pipe_t string is no longer valid and all the 569 | * references must be substituted with the new pointer returned by the call. */ 570 | pipe_t pipe_catrepr(pipe_t s, const char *p, size_t len) 571 | { 572 | s = pipe_catlen(s,"\"",1); 573 | while(len--) { 574 | switch(*p) { 575 | case '\\': 576 | case '"': 577 | s = pipe_catprintf(s,"\\%c",*p); 578 | break; 579 | case '\n': 580 | s = pipe_catlen(s,"\\n",2); 581 | break; 582 | case '\r': 583 | s = pipe_catlen(s,"\\r",2); 584 | break; 585 | case '\t': 586 | s = pipe_catlen(s,"\\t",2); 587 | break; 588 | case '\a': 589 | s = pipe_catlen(s,"\\a",2); 590 | break; 591 | case '\b': 592 | s = pipe_catlen(s,"\\b",2); 593 | break; 594 | default: 595 | if (isprint(*p)) 596 | s = pipe_catprintf(s,"%c",*p); 597 | else 598 | s = pipe_catprintf(s,"\\x%02x",(unsigned char)*p); 599 | break; 600 | } 601 | p++; 602 | } 603 | return pipe_catlen(s,"\"",1); 604 | } 605 | 606 | /* Helper function for pipesplitargs() that returns non zero if 'c' 607 | * is a valid hex digit. */ 608 | static int is_hex_digit(char c) 609 | { 610 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 611 | (c >= 'A' && c <= 'F'); 612 | } 613 | 614 | /* Helper function for pipesplitargs() that converts a hex digit into an 615 | * integer from 0 to 15 */ 616 | static int hex_digit_to_int(char c) 617 | { 618 | switch(c) { 619 | case '0': 620 | return 0; 621 | case '1': 622 | return 1; 623 | case '2': 624 | return 2; 625 | case '3': 626 | return 3; 627 | case '4': 628 | return 4; 629 | case '5': 630 | return 5; 631 | case '6': 632 | return 6; 633 | case '7': 634 | return 7; 635 | case '8': 636 | return 8; 637 | case '9': 638 | return 9; 639 | case 'a': 640 | case 'A': 641 | return 10; 642 | case 'b': 643 | case 'B': 644 | return 11; 645 | case 'c': 646 | case 'C': 647 | return 12; 648 | case 'd': 649 | case 'D': 650 | return 13; 651 | case 'e': 652 | case 'E': 653 | return 14; 654 | case 'f': 655 | case 'F': 656 | return 15; 657 | default: 658 | return 0; 659 | } 660 | } 661 | 662 | /* Split a line into arguments, where every argument can be in the 663 | * following programming-language REPL-alike form: 664 | * 665 | * foo bar "newline are supported\n" and "\xff\x00otherstuff" 666 | * 667 | * The number of arguments is stored into *argc, and an array 668 | * of pipe_t is returned. 669 | * 670 | * The caller should free the resulting array of pipe_t strings with 671 | * pipe_freesplitres(). 672 | * 673 | * Note that pipe_catrepr() is able to convert back a string into 674 | * a quoted string in the same format pipe_splitargs() is able to parse. 675 | * 676 | * The function returns the allocated tokens on success, even when the 677 | * input string is empty, or NULL if the input contains unbalanced 678 | * quotes or closed quotes followed by non space characters 679 | * as in: "foo"bar or "foo' 680 | */ 681 | pipe_t *pipe_splitargs(const char *line, int *argc) 682 | { 683 | const char *p = line; 684 | char *current = NULL; 685 | char **vector = NULL; 686 | 687 | *argc = 0; 688 | while(1) { 689 | /* skip blanks */ 690 | while(*p && isspace(*p)) p++; 691 | if (*p) { 692 | /* get a token */ 693 | int inq=0; /* set to 1 if we are in "quotes" */ 694 | int insq=0; /* set to 1 if we are in 'single quotes' */ 695 | int done=0; 696 | 697 | if (current == NULL) current = pipe_empty(); 698 | while(!done) { 699 | if (inq) { 700 | if (*p == '\\' && *(p+1) == 'x' && 701 | is_hex_digit(*(p+2)) && 702 | is_hex_digit(*(p+3))) { 703 | unsigned char byte; 704 | 705 | byte = (hex_digit_to_int(*(p+2))*16)+ 706 | hex_digit_to_int(*(p+3)); 707 | current = pipe_catlen(current,(char*)&byte,1); 708 | p += 3; 709 | } else if (*p == '\\' && *(p+1)) { 710 | char c; 711 | 712 | p++; 713 | switch(*p) { 714 | case 'n': 715 | c = '\n'; 716 | break; 717 | case 'r': 718 | c = '\r'; 719 | break; 720 | case 't': 721 | c = '\t'; 722 | break; 723 | case 'b': 724 | c = '\b'; 725 | break; 726 | case 'a': 727 | c = '\a'; 728 | break; 729 | default: 730 | c = *p; 731 | break; 732 | } 733 | current = pipe_catlen(current,&c,1); 734 | } else if (*p == '"') { 735 | /* closing quote must be followed by a space or 736 | * nothing at all. */ 737 | if (*(p+1) && !isspace(*(p+1))) goto err; 738 | done=1; 739 | } else if (!*p) { 740 | /* unterminated quotes */ 741 | goto err; 742 | } else { 743 | current = pipe_catlen(current,p,1); 744 | } 745 | } else if (insq) { 746 | if (*p == '\\' && *(p+1) == '\'') { 747 | p++; 748 | current = pipe_catlen(current,"'",1); 749 | } else if (*p == '\'') { 750 | /* closing quote must be followed by a space or 751 | * nothing at all. */ 752 | if (*(p+1) && !isspace(*(p+1))) goto err; 753 | done=1; 754 | } else if (!*p) { 755 | /* unterminated quotes */ 756 | goto err; 757 | } else { 758 | current = pipe_catlen(current,p,1); 759 | } 760 | } else { 761 | switch(*p) { 762 | case ' ': 763 | case '\n': 764 | case '\r': 765 | case '\t': 766 | case '\0': 767 | done=1; 768 | break; 769 | case '"': 770 | inq=1; 771 | break; 772 | case '\'': 773 | insq=1; 774 | break; 775 | default: 776 | current = pipe_catlen(current,p,1); 777 | break; 778 | } 779 | } 780 | if (*p) p++; 781 | } 782 | /* add the token to the vector */ 783 | vector = realloc(vector,((*argc)+1)*sizeof(char*)); 784 | vector[*argc] = current; 785 | (*argc)++; 786 | current = NULL; 787 | } else { 788 | /* Even on empty input string return something not NULL. */ 789 | if (vector == NULL) vector = malloc(sizeof(void*)); 790 | return vector; 791 | } 792 | } 793 | 794 | err: 795 | while((*argc)--) 796 | pipe_free(vector[*argc]); 797 | free(vector); 798 | if (current) pipe_free(current); 799 | *argc = 0; 800 | return NULL; 801 | } 802 | 803 | /* Modify the string substituting all the occurrences of the set of 804 | * characters specified in the 'from' string to the corresponding character 805 | * in the 'to' array. 806 | * 807 | * For instance: pipe_mapchars(mystring, "ho", "01", 2) 808 | * will have the effect of turning the string "hello" into "0ell1". 809 | * 810 | * The function returns the pipe_t string pointer, that is always the same 811 | * as the input pointer since no resize is needed. */ 812 | pipe_t pipe_mapchars(pipe_t s, const char *from, const char *to, size_t setlen) 813 | { 814 | size_t j, i, l = pipe_len(s); 815 | 816 | for (j = 0; j < l; j++) { 817 | for (i = 0; i < setlen; i++) { 818 | if (s[j] == from[i]) { 819 | s[j] = to[i]; 820 | break; 821 | } 822 | } 823 | } 824 | return s; 825 | } 826 | 827 | /* Join an array of C strings using the specified separator (also a C string). 828 | * Returns the result as an pipe_t string. */ 829 | pipe_t pipe_join(char **argv, int argc, char *sep, size_t seplen) 830 | { 831 | pipe_t join = pipe_empty(); 832 | int j; 833 | 834 | for (j = 0; j < argc; j++) { 835 | join = pipe_cat(join, argv[j]); 836 | if (j != argc-1) join = pipe_catlen(join,sep,seplen); 837 | } 838 | return join; 839 | } 840 | 841 | /* Like pipejoin, but joins an array of pipe strings. */ 842 | pipe_t pipe_joinpipe(pipe_t *argv, int argc, const char *sep, size_t seplen) 843 | { 844 | pipe_t join = pipe_empty(); 845 | int j; 846 | 847 | for (j = 0; j < argc; j++) { 848 | join = pipe_catpipe(join, argv[j]); 849 | if (j != argc-1) join = pipe_catlen(join,sep,seplen); 850 | } 851 | return join; 852 | } -------------------------------------------------------------------------------- /src/hb_pipe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hashbase - https://github.com/MaciejCzyzewski/hashbase 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2014 Maciej A. Czyzewski 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Author: Maciej A. Czyzewski 26 | */ 27 | 28 | #ifndef _HB_PIPE_H_ 29 | #define _HB_PIPE_H_ 30 | 31 | #include 32 | #include 33 | 34 | typedef char *pipe_t; 35 | 36 | struct pipe_hdr_t { 37 | int len; 38 | int free; 39 | char buf[]; 40 | }; 41 | 42 | pipe_t pipe_newlen(const void *init, size_t initlen); 43 | pipe_t pipe_new(const char *init); 44 | pipe_t pipe_empty(void); 45 | size_t pipe_len(const pipe_t s); 46 | pipe_t pipe_dup(const pipe_t s); 47 | void pipe_free(pipe_t s); 48 | size_t pipe_avail(const pipe_t s); 49 | pipe_t pipe_growzero(pipe_t s, size_t len); 50 | pipe_t pipe_catlen(pipe_t s, const void *t, size_t len); 51 | pipe_t pipe_cat(pipe_t s, const char *t); 52 | pipe_t pipe_catpipe(pipe_t s, const pipe_t t); 53 | pipe_t pipe_cpylen(pipe_t s, const char *t, size_t len); 54 | pipe_t pipe_cpy(pipe_t s, const char *t); 55 | 56 | pipe_t pipe_catvprintf(pipe_t s, const char *fmt, va_list ap); 57 | #ifdef __GNUC__ 58 | pipe_t pipe_catprintf(pipe_t s, const char *fmt, ...) 59 | __attribute__((format(printf, 2, 3))); 60 | #else 61 | pipe_t pipe_catprintf(pipe_t s, const char *fmt, ...); 62 | #endif 63 | 64 | void pipe_trim(pipe_t s, const char *cset); 65 | void pipe_range(pipe_t s, int start, int end); 66 | void pipe_updatelen(pipe_t s); 67 | void pipe_clear(pipe_t s); 68 | int pipe_cmp(const pipe_t s1, const pipe_t s2); 69 | pipe_t *pipe_splitlen(const char *s, int len, const char *sep, int seplen, int *count); 70 | void pipe_freesplitres(pipe_t *tokens, int count); 71 | void pipe_tolower(pipe_t s); 72 | void pipe_toupper(pipe_t s); 73 | pipe_t pipe_fromlonglong(long long value); 74 | pipe_t pipe_catrepr(pipe_t s, const char *p, size_t len); 75 | pipe_t *pipe_splitargs(const char *line, int *argc); 76 | pipe_t pipe_mapchars(pipe_t s, const char *from, const char *to, size_t setlen); 77 | pipe_t pipe_join(char **argv, int argc, char *sep, size_t seplen); 78 | pipe_t pipe_joinpipe(pipe_t *argv, int argc, const char *sep, size_t seplen); 79 | 80 | /* Low level functions exposed to the user API */ 81 | pipe_t pipe_MakeRoomFor(pipe_t s, size_t addlen); 82 | void pipe_IncrLen(pipe_t s, int incr); 83 | pipe_t pipe_RemoveFreeSpace(pipe_t s); 84 | size_t pipe_AllocSize(pipe_t s); 85 | 86 | #endif -------------------------------------------------------------------------------- /src/hb_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * UTIL A utility functions. 3 | * 4 | * Version: @(#)util.c 0.0.1 09/07/14 5 | * Authors: Maciej A. Czyzewski, 6 | * 7 | */ 8 | 9 | -------------------------------------------------------------------------------- /src/hb_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hashbase - https://github.com/MaciejCzyzewski/hashbase 3 | * 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2014 Maciej A. Czyzewski 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | * Author: Maciej A. Czyzewski 26 | */ 27 | 28 | #ifndef _HB_UTIL_H_ 29 | #define _HB_UTIL_H_ 30 | 31 | #define KB (1024) 32 | #define MB (1024 * KB) 33 | #define GB (1024 * MB) 34 | 35 | #define CRLF "\x0d\x0a" 36 | 37 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 38 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 39 | 40 | #define COUNT(a) (sizeof(a) / sizeof(*(a))) 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /tests/Python/hashbase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import socket # hashbase 4 | 5 | class hashbase: 6 | def __init__(self): 7 | self.buffer = 1024 8 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 9 | 10 | def connect(self, host, port): 11 | self.host = str(host) 12 | self.port = int(port) 13 | 14 | self.socket.connect((self.host, self.port)) 15 | 16 | def set(self, key, value): 17 | self.socket.send("set \"" + str(key) + "\" \"" + str(value) + "\"\r\n") 18 | return self.socket.recv(self.buffer)[:-2] 19 | 20 | def get(self, key): 21 | self.socket.send("get \"" + str(key) + "\"\r\n") 22 | return self.socket.recv(self.buffer)[:-2] 23 | 24 | def delete(self, key): # del -> delete 25 | self.socket.send("del \"" + str(key) + "\"\r\n") 26 | return self.socket.recv(self.buffer)[:-2] -------------------------------------------------------------------------------- /tests/Python/tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import hashbase # hashbase 4 | import sys 5 | 6 | if len(sys.argv) != 3: 7 | print "Usage: python tests.py " 8 | raise SystemExit 9 | 10 | # Connect 11 | hb = hashbase.hashbase() 12 | hb.connect(sys.argv[1], sys.argv[2]) 13 | 14 | # Write 15 | hb.set("foo", "bar") 16 | hb.set("maciej a.", "czyzewski") 17 | hb.set("delete", "me") 18 | hb.set("plus", "minus") 19 | 20 | # Delete 21 | hb.delete("delete") 22 | 23 | # Get 24 | print hb.get("foo") # bar 25 | print hb.get("maciej a.") # czyzewski 26 | print hb.get("delete") # -1 27 | print hb.get("plus") # minus -------------------------------------------------------------------------------- /tests/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import socket # hashbase 4 | import sys 5 | 6 | ########################################################### 7 | # Command-line interface # 8 | ########################################################### 9 | # >> | Client input # 10 | # => | Server output # 11 | ########################################################### 12 | 13 | if len(sys.argv) != 3: 14 | print "Usage: python client.py " 15 | raise SystemExit 16 | 17 | TCP_IP = str(sys.argv[1]) 18 | TCP_PORT = int(sys.argv[2]) 19 | BUFFER_SIZE = 1024 20 | 21 | try: 22 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 23 | s.connect((TCP_IP, TCP_PORT)) 24 | except: 25 | print "Error: Cannot connect to the hashbase..." 26 | raise SystemExit 27 | 28 | def run(): 29 | while True: 30 | try: 31 | g = raw_input(">> ") 32 | except KeyboardInterrupt: 33 | print "Warning: Closing connection..." 34 | raise SystemExit 35 | 36 | s.send(g + "\r\n") 37 | r = s.recv(BUFFER_SIZE)[:-2] 38 | print "=>", r 39 | 40 | s.close() 41 | 42 | run() --------------------------------------------------------------------------------