This commit is contained in:
commit
776f21257a
192
.clang-format
Normal file
192
.clang-format
Normal file
@ -0,0 +1,192 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: true
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 140
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
QualifierAlignment: Leave
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
PackConstructorInitializers: BinPack
|
||||
BasedOnStyle: ''
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequires: false
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Right
|
||||
PPIndentWidth: -1
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Latest
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
|
29
.github/workflows/make-rlib-single-platform.yml
vendored
Normal file
29
.github/workflows/make-rlib-single-platform.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Build and run rrex2
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install clang-format -y
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}
|
||||
run: make
|
||||
|
||||
- name: Install
|
||||
working-directory: ${{github.workspace}}
|
||||
run: make install
|
||||
|
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
a.out
|
||||
.vscore
|
||||
.bzr
|
||||
.bzrignore
|
||||
.vscode
|
||||
C-Data-Structs
|
||||
rasmd
|
||||
*yur*
|
||||
yur*
|
||||
hashmap.h
|
||||
primes.h
|
||||
errors.h
|
||||
*.tty
|
||||
*.o
|
||||
build
|
||||
.history
|
||||
|
213
Makefile
Normal file
213
Makefile
Normal file
@ -0,0 +1,213 @@
|
||||
# Compiler
|
||||
CC = gcc
|
||||
|
||||
# Compiler flags
|
||||
CFLAGS = -Wall -Wextra -Werror -Ofast -std=c2x -lm -lrt -pthread -fPIC
|
||||
LDFLAGS = -lm
|
||||
|
||||
# -D_POSIX_C_SOURCE=199309L
|
||||
all: clear test_rbuffer test_rliza test_rnet test_rmalloc test_rtemp test_rjson test_rrex4 test_rstring_list test_rhttp test_rtime test_arena test_rtree test_rstring test_rlexer test_rrex3 test_rio test_rhashtable test_rkeytable test_rterminal test_rmerge run_rmerge format_all build format_all test_rlib
|
||||
|
||||
|
||||
|
||||
|
||||
format_all: format_rlib_h
|
||||
clang-format *.c *.h *.cpp -i --sort-includes=false
|
||||
|
||||
format_rlib_h:
|
||||
clang-format rlib.h build/rlib.h -i --sort-includes --verbose
|
||||
|
||||
clear:
|
||||
-@rm -rf build
|
||||
@mkdir build
|
||||
@echo "Build directory emptied"
|
||||
|
||||
rebuild: clear all
|
||||
|
||||
test_rbuffer: build_rbuffer run_rbuffer
|
||||
build_rbuffer:
|
||||
$(CC) $(CFLAGS) rbuffer.c -o ./build/rbuffer
|
||||
run_rbuffer:
|
||||
./build/rbuffer test
|
||||
|
||||
test_rliza: build_rliza run_rliza
|
||||
build_rliza:
|
||||
$(CC) $(CFLAGS) rliza.c -o ./build/rliza
|
||||
run_rliza:
|
||||
./build/rliza test
|
||||
|
||||
test_rnet: build_rnet run_rnet
|
||||
build_rnet:
|
||||
$(CC) $(CFLAGS) rnet.c -o ./build/rnet
|
||||
run_rnet:
|
||||
./build/rnet test
|
||||
|
||||
test_rtemp: build_rtemp run_rtemp
|
||||
build_rtemp:
|
||||
$(CC) $(CFLAGS) rtemp.c -o ./build/rtemp
|
||||
run_rtemp:
|
||||
./build/rtemp
|
||||
|
||||
test_rjson: build_rjson run_rjson
|
||||
build_rjson:
|
||||
$(CC) $(CFLAGS) rjson.c -o ./build/rjson
|
||||
run_rjson:
|
||||
./build/rjson
|
||||
|
||||
|
||||
test_rstring_list: build_rstring_list run_rstring_list
|
||||
build_rstring_list:
|
||||
$(CC) $(CFLAGS) rstring_list.c -o ./build/rstring_list
|
||||
run_rstring_list:
|
||||
./build/rstring_list
|
||||
|
||||
test_rhttp: build_rhttp run_rhttp
|
||||
build_rhttp:
|
||||
$(CC) $(CFLAGS) rhttp.c -o ./build/rhttp
|
||||
run_rhttp:
|
||||
./build/rhttp
|
||||
|
||||
test_rautocomplete: build_rautocomplete run_rautocomplete
|
||||
build_rautocomplete:
|
||||
$(CC) $(CFLAGS) rautocomplete.c -o ./build/rautocomplete
|
||||
run_rautocomplete:
|
||||
./build/rautocomplete
|
||||
|
||||
test_rlexer: build_rlexer run_rlexer
|
||||
build_rlexer:
|
||||
$(CC) $(CFLAGS) rlexer.c -o ./build/rlexer
|
||||
run_rlexer:
|
||||
./build/rlexer
|
||||
|
||||
|
||||
test_rrex4: build_rrex4 run_rrex4
|
||||
build_rrex4:
|
||||
$(CC) $(CFLAGS) rrex4.c -o ./build/rrex4
|
||||
run_rrex4:
|
||||
./build/rrex4
|
||||
|
||||
test_rrex3: build_rrex3 run_rrex3
|
||||
build_rrex3:
|
||||
$(CC) $(CFLAGS) rrex3.c -o ./build/rrex3
|
||||
run_rrex3:
|
||||
./build/rrex3
|
||||
|
||||
test_rio: build_rio run_rio
|
||||
build_rio:
|
||||
$(CC) $(CFLAGS) rio.c -o ./build/rio
|
||||
run_rio:
|
||||
./build/rio
|
||||
|
||||
|
||||
|
||||
test_rterm: build_rterm run_rterm
|
||||
build_rterm:
|
||||
$(CC) $(CFLAGS) rterm.c -o ./build/rterm
|
||||
run_rterm:
|
||||
./build/rterm
|
||||
|
||||
test_rterminal: build_rterminal run_rterminal
|
||||
build_rterminal:
|
||||
$(CC) $(CFLAGS) rterminal.c -o ./build/rterminal
|
||||
run_rterminal:
|
||||
./build/rterminal
|
||||
|
||||
test_rmerge: build_rmerge run_rmerge
|
||||
build_rmerge:
|
||||
$(CC) $(CFLAGS) rmerge.c -o ./build/rmerge
|
||||
run_rmerge:
|
||||
./build/rmerge _rlib.h > ./build/rlib.h
|
||||
cp ./build/rlib.h ./rlib.h
|
||||
|
||||
test_rprint: build_rprint run_rprint
|
||||
build_rprint:
|
||||
$(CC) $(CFLAGS) rprint.c -o ./build/rprint
|
||||
run_rprint:
|
||||
./build/rprint
|
||||
|
||||
test_rstring: build_rstring run_rstring
|
||||
build_rstring:
|
||||
$(CC) $(CFLAGS) rstring.c -o ./build/rstring
|
||||
run_rstring:
|
||||
./build/rstring
|
||||
|
||||
test_rbench: build_rbench run_rbench
|
||||
build_rbench:
|
||||
$(CC) $(CFLAGS) rbench.c -o ./build/rbench
|
||||
run_rbench:
|
||||
./build/rbench
|
||||
|
||||
test_rbench_cpp: build_rbench_cpp run_rbench_cpp
|
||||
build_rbench_cpp:
|
||||
g++ $(CFLAGS) ./C-Data-Structs/src/primes.c rbench.cpp -o ./build/rbench.cpp -I./C-Data-Structs/include
|
||||
run_rbench_cpp:
|
||||
./build/rbench.cpp
|
||||
|
||||
test_yurii_cpp: format_all build_yurii_cpp run_yurii_cpp
|
||||
build_yurii_cpp:
|
||||
rmerge rbench.cpp > yurii_hashmap.cpp
|
||||
g++ C-Data-Structs/src/primes.c yurii_hashmap.cpp -o ./build/yurii_hashmap.cpp -IC-Data-Structs/include
|
||||
run_yurii_cpp:
|
||||
./build/yurii_hashmap.cpp
|
||||
|
||||
test_rmalloc: build_rmalloc run_rmalloc
|
||||
build_rmalloc:
|
||||
$(CC) $(CFLAGS) rmalloc.c -o ./build/rmalloc
|
||||
run_rmalloc:
|
||||
./build/rmalloc
|
||||
|
||||
test_rtime: build_rtime run_rtime
|
||||
build_rtime:
|
||||
$(CC) $(CFLAGS) rtime.c -o ./build/rtime
|
||||
run_rtime:
|
||||
./build/rtime
|
||||
|
||||
test_arena: build_arena run_arena
|
||||
build_arena:
|
||||
$(CC) $(CFLAGS) arena.c -o ./build/arena
|
||||
run_arena:
|
||||
./build/arena
|
||||
|
||||
test_rlib: build_rlib run_rlib
|
||||
build_rlib:
|
||||
$(CC) $(CFLAGS) rlib.c -o ./build/rlib
|
||||
run_rlib:
|
||||
./build/rlib test
|
||||
|
||||
test_rtree: build_rtree run_rtree
|
||||
build_rtree:
|
||||
$(CC) $(CFLAGS) rtree.c -o ./build/rtree
|
||||
run_rtree:
|
||||
./build/rtree
|
||||
|
||||
test_rhashtable: build_rhashtable run_rhashtable
|
||||
build_rhashtable:
|
||||
$(CC) $(CFLAGS) rhashtable.c -o ./build/rhashtable
|
||||
run_rhashtable:
|
||||
./build/rhashtable
|
||||
|
||||
test_rkeytable: build_rkeytable run_rkeytable
|
||||
build_rkeytable:
|
||||
$(CC) $(CFLAGS) rkeytable.c -o ./build/rkeytable
|
||||
run_rkeytable:
|
||||
./build/rkeytable
|
||||
|
||||
build: format_rlib_h
|
||||
cp ./clean build/clean
|
||||
cp rlib.h rlibrlibso.c
|
||||
@gcc rlibrlibso.c $(CFLAGS) -shared -o ./build/librlib.so
|
||||
@echo "Built a new rlib.so"
|
||||
@gcc rlibso.c -L./build -Wl,-rpath=. -lrlib -o ./build/rlibso ${CFLAGS}
|
||||
@cd ./build && ./rlibso
|
||||
@echo "Build succesful"
|
||||
|
||||
install:
|
||||
sudo cp ./build/rlib.h /usr/include/rlib.h
|
||||
sudo cp ./build/rmerge /usr/local/bin/rmerge
|
||||
sudo cp ./build/clean /usr/local/bin/clean
|
||||
sudo cp ./build/rlib /usr/local/bin/rlib
|
||||
|
||||
publish:
|
||||
brz add
|
||||
brz commit
|
||||
brz push lp:rlib
|
94
README.md
Normal file
94
README.md
Normal file
@ -0,0 +1,94 @@
|
||||
# RLIB
|
||||
|
||||
Collection of frequently used C functions by me.
|
||||
|
||||
It contains
|
||||
- An advanced benchmark system.
|
||||
- An alternative to printf supporting printing of time between messages for e.g. benchmarking.
|
||||
and colors.
|
||||
- Super fast tree map (much faster than hash table).
|
||||
- Stdout redirection.
|
||||
- Terminal progress bar.
|
||||
- Multi purpose lexer.
|
||||
- Custom malloc for counting allocations and free's.
|
||||
- Simple test library that works like assert that also checks memory if rmalloc is used and
|
||||
creates summary and provides exit code.
|
||||
- Several time functions supporting nano seconds.
|
||||
- Several math functions for if not available by std (on termux for example).
|
||||
- Arena blazing fast memory.
|
||||
|
||||
## Extensive tested and benchmarked
|
||||
All files get merged together to one file called `rlib.c` that can be used as a header file.
|
||||
The result is a single header framework.
|
||||
![Gif of build process](build.gif)
|
||||
|
||||
## ENVIRONMENT VARIABLES
|
||||
|
||||
### Disabling color
|
||||
Set env RDISABLE_COLORS = 1 to disable color on stdout. stderr will still have colors for gui.
|
||||
|
||||
## Compiler and Flags
|
||||
- **`CC = gcc`**: Specifies the C compiler (GCC).
|
||||
- **`CFLAGS = -Wall -Wextra -Ofast`**: Compiler flags:
|
||||
- `-Wall`: Enable all warnings.
|
||||
- `-Wextra`: Enable extra warnings.
|
||||
- `-Ofast`: Optimize for speed.
|
||||
- **`LDFLAGS = -lm`**: Linker flags to link with the math library.
|
||||
|
||||
## Main Targets
|
||||
|
||||
### `all:`
|
||||
Runs the following steps in sequence:
|
||||
- Testing (`test_*` targets).
|
||||
- Formatting code (`format_all`).
|
||||
- Building the project (`build`).
|
||||
- Installing executables (`install`).
|
||||
|
||||
### `format_all:`
|
||||
Runs `clang-format` to format all `.c`, `.h`, and `.cpp` files in the directory.
|
||||
|
||||
### `build:`
|
||||
Builds the `rlib` shared library (`librlib.so`) and an executable `rlibso` that uses this library:
|
||||
- Compiles `rlib.c` into `librlib.so`.
|
||||
- Compiles `rlibso.c` linking it with `librlib.so`.
|
||||
- Runs the `rlibso` executable.
|
||||
|
||||
### `install:`
|
||||
Installs the `rmerge` and `clean` executables to `/usr/bin` using `sudo`.
|
||||
|
||||
## Testing Targets (`test_*`)
|
||||
|
||||
Each `test_*` target follows these steps:
|
||||
|
||||
### `build_*:`
|
||||
Compiles a specific `.c` file into an executable in the `./build` directory.
|
||||
|
||||
### `run_*:`
|
||||
Runs the corresponding executable.
|
||||
|
||||
Example for `rlexer`:
|
||||
- **`build_rlexer:`** Compiles `rlexer.c` to `./build/rlexer`.
|
||||
- **`run_rlexer:`** Runs `./build/rlexer`.
|
||||
|
||||
## Backup
|
||||
|
||||
### `backup:`
|
||||
Compresses the source files, Makefile, and additional project files into a `rlib.rzip` archive.
|
||||
|
||||
## Special Cases
|
||||
|
||||
### `test_rbench_cpp:`
|
||||
Builds and runs a C++ benchmark program:
|
||||
- Compiles `rbench.cpp` along with other necessary files.
|
||||
- Runs the resulting executable.
|
||||
|
||||
## Other Targets
|
||||
|
||||
### `format_rlib_c:`
|
||||
Specifically formats the `rlib.c` file.
|
||||
|
||||
### `build_*:`
|
||||
Targets that compile source files into executables.
|
||||
|
||||
### `run_*:`
|
||||
Targets that run the compiled executables.
|
45
_rlib.h
Normal file
45
_rlib.h
Normal file
@ -0,0 +1,45 @@
|
||||
#include "license.h"
|
||||
#ifndef RLIB_H
|
||||
#define RLIB_H
|
||||
// BEGIN OF RLIB
|
||||
|
||||
/*
|
||||
* Line below will be filtered by rmerge
|
||||
<script language="Javva script" type="woeiii" src="Pony.html" after-tag="after
|
||||
tag" />
|
||||
*/
|
||||
#include "rtypes.h"
|
||||
#include "nsock.h"
|
||||
#include "rmalloc.h"
|
||||
#include "uuid.h"
|
||||
#include "rnet.h"
|
||||
#include "rargs.h"
|
||||
#include "rcat.h"
|
||||
#include "rliza.h"
|
||||
#include "rcov.h"
|
||||
#include "rtemp.h"
|
||||
#include "rhttp.h"
|
||||
#include "rjson.h"
|
||||
#include "rstring_list.h"
|
||||
#include "rautocomplete.h"
|
||||
#include "rrex4.h"
|
||||
#include "rprint.h"
|
||||
#include "rmath.h"
|
||||
#include "rtest.h"
|
||||
#include "rkeytable.h"
|
||||
#include "rhashtable.h"
|
||||
#include "rstring_list.h"
|
||||
#include "rrex3.h"
|
||||
#include "rtime.h"
|
||||
#include "arena.h"
|
||||
#include "rio.h"
|
||||
#include "rstring.h"
|
||||
#include "rcase.h"
|
||||
#include "rterminal.h"
|
||||
#include "rterm.h"
|
||||
#include "rtree.h"
|
||||
#include "rlexer.h"
|
||||
#include "rbench.h"
|
||||
#include "main.h"
|
||||
// END OF RLIB
|
||||
#endif
|
35
arena.c
Normal file
35
arena.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include "arena.h"
|
||||
#include "rtest.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
rtest_banner("testing arena");
|
||||
arena_t *arena = arena_construct();
|
||||
// Test initial data
|
||||
rtest_banner("Initial values");
|
||||
rtest_assert(arena->memory == NULL);
|
||||
rtest_assert(arena->size == 0);
|
||||
rtest_assert(arena->pointer == 0);
|
||||
arena_free(arena);
|
||||
// New instance test
|
||||
rtest_banner("New instance defaults");
|
||||
arena = arena_new(1024);
|
||||
rtest_assert(arena->memory != NULL);
|
||||
rtest_assert(arena->size == 1024);
|
||||
rtest_assert(arena->pointer == 0);
|
||||
arena_free(arena);
|
||||
// Allocate test
|
||||
rtest_banner("Allocate");
|
||||
arena = arena_new(1024);
|
||||
int *int_one = (int *)arena_alloc(arena, sizeof(int));
|
||||
*int_one = 10;
|
||||
rtest_assert(*int_one == 10);
|
||||
rtest_assert(arena->pointer == sizeof(int));
|
||||
int *int_two = (int *)arena_alloc(arena, sizeof(int));
|
||||
*int_two = 20;
|
||||
rtest_assert(*int_two == 20);
|
||||
rtest_assert(arena->pointer == sizeof(int) * 2);
|
||||
arena_free(arena);
|
||||
return rtest_end("");
|
||||
}
|
47
arena.h
Normal file
47
arena.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef RARENA_H
|
||||
#define RARENA_H
|
||||
|
||||
#include "rmalloc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct arena_t {
|
||||
unsigned char *memory;
|
||||
unsigned int pointer;
|
||||
unsigned int size;
|
||||
} arena_t;
|
||||
|
||||
arena_t *arena_construct() {
|
||||
arena_t *arena = (arena_t *)rmalloc(sizeof(arena_t));
|
||||
arena->memory = NULL;
|
||||
arena->pointer = 0;
|
||||
arena->size = 0;
|
||||
return arena;
|
||||
}
|
||||
|
||||
arena_t *arena_new(size_t size) {
|
||||
arena_t *arena = arena_construct();
|
||||
arena->memory = (unsigned char *)rmalloc(size);
|
||||
arena->size = size;
|
||||
return arena;
|
||||
}
|
||||
|
||||
void *arena_alloc(arena_t *arena, size_t size) {
|
||||
if (arena->pointer + size > arena->size) {
|
||||
return NULL;
|
||||
}
|
||||
void *p = arena->memory + arena->pointer;
|
||||
arena->pointer += size;
|
||||
return p;
|
||||
}
|
||||
|
||||
void arena_free(arena_t *arena) {
|
||||
// Just constructed and unused arena memory is NULL so no free needed
|
||||
if (arena->memory) {
|
||||
rfree(arena->memory);
|
||||
}
|
||||
rfree(arena);
|
||||
}
|
||||
|
||||
void arena_reset(arena_t *arena) { arena->pointer = 0; }
|
||||
#endif
|
42
clean
Executable file
42
clean
Executable file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
delete_files = "--delete" in sys.argv
|
||||
|
||||
exclude_files = ["Makefile", ".gitignore", ".bzrignore", "clean",".clang-format",".clang-tidy"]
|
||||
source_extensions = [".bak", ".tar",".c",".h",".cpp",".hpp",".md",".rzip",".gif"]
|
||||
|
||||
files_ignored = []
|
||||
files_pending = []
|
||||
|
||||
file_count = 0
|
||||
|
||||
for f in pathlib.Path(".").glob("*"):
|
||||
if f.is_dir():
|
||||
files_ignored.append(f)
|
||||
continue
|
||||
if f.name in exclude_files:
|
||||
files_ignored.append(f)
|
||||
continue
|
||||
if f.suffix not in source_extensions:
|
||||
file_count += 1
|
||||
files_pending.append(f)
|
||||
if delete_files:
|
||||
f.unlink();
|
||||
print("{} - DELETED".format(str(f)))
|
||||
|
||||
if not delete_files:
|
||||
|
||||
print("IGNORED:")
|
||||
for f in files_ignored:
|
||||
print(" - {}".format(str(f)))
|
||||
|
||||
print("PENDING DELETION:")
|
||||
for f in files_pending:
|
||||
print(" - {}".format(str(f)))
|
||||
|
||||
print("{} files.".format(file_count))
|
||||
|
||||
if not delete_files:
|
||||
print("\n* Add --delete to remove files listed above.")
|
22
license.h
Normal file
22
license.h
Normal file
@ -0,0 +1,22 @@
|
||||
// MIT License
|
||||
// ===========
|
||||
|
||||
// Copyright (c) 2024 Retoor
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
51
main.h
Normal file
51
main.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef RLIB_MAIN
|
||||
#define RLIB_MAIN
|
||||
#include "rhttp.h"
|
||||
#include "rmerge.h"
|
||||
#include "rcov.h"
|
||||
#include "rcase.h"
|
||||
|
||||
void forward_argument(int *argcc, char *argv[]) {
|
||||
int argc = *argcc;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argv[i] = argv[i + 1];
|
||||
}
|
||||
argc--;
|
||||
*argcc = argc;
|
||||
}
|
||||
|
||||
int rlib_main(int argc, char *argv[]) {
|
||||
|
||||
if (argc == 1) {
|
||||
printf("rlib\n\n");
|
||||
printf("options:\n");
|
||||
printf(" httpd - a http file server. Accepts port as argument.\n");
|
||||
printf(" rmerge - a merge tool. Converts c source files to one file \n"
|
||||
" with local includes by giving main file as argument.\n");
|
||||
printf(" rcov - coverage tool theat cleans up after himself. Based on "
|
||||
"lcov.\n");
|
||||
printf(" rcase - tool to swap input file automatically between"
|
||||
" camel case and snake case.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
forward_argument(&argc, argv);
|
||||
|
||||
if (!strcmp(argv[0], "httpd")) {
|
||||
|
||||
return rhttp_main(argc, argv);
|
||||
}
|
||||
if (!strcmp(argv[0], "rmerge")) {
|
||||
return rmerge_main(argc, argv);
|
||||
}
|
||||
if (!strcmp(argv[0], "rcov")) {
|
||||
return rcov_main(argc, argv);
|
||||
}
|
||||
if (!strcmp(argv[0], "rcase")) {
|
||||
return rcase_main(argc, argv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
11
nsock.c
Normal file
11
nsock.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include "nsock.h"
|
||||
|
||||
void on_connect(int fd) { printf("connect\n"); }
|
||||
void on_data(int fd) { printf("data\n"); }
|
||||
void on_close(int fd) { printf("close\n"); }
|
||||
|
||||
int main() {
|
||||
|
||||
nsock(9999, on_connect, on_data, on_close);
|
||||
return 0;
|
||||
}
|
419
nsock.h
Normal file
419
nsock.h
Normal file
@ -0,0 +1,419 @@
|
||||
#ifndef NSOCK_H
|
||||
#define NSOCK_H
|
||||
#include "rmalloc.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include "rio.h"
|
||||
|
||||
int *nsock_socks = NULL;
|
||||
int *nsock_readable = NULL;
|
||||
void **nsock_data = NULL;
|
||||
int nsock_server_fd = 0;
|
||||
int nsock_max_socket_fd = 0;
|
||||
|
||||
typedef enum nsock_type_t { NSOCK_NONE = 0, NSOCK_SERVER, NSOCK_CLIENT, NSOCK_UPSTREAM } nsock_type_t;
|
||||
|
||||
typedef struct nsock_it {
|
||||
int fd;
|
||||
int *upstreams;
|
||||
bool connected;
|
||||
bool downstream;
|
||||
unsigned int upstream_count;
|
||||
nsock_type_t type;
|
||||
} nsock_t;
|
||||
|
||||
nsock_t **nsocks = NULL;
|
||||
int nsocks_count = 0;
|
||||
|
||||
void (*nsock_on_connect)(int fd) = NULL;
|
||||
void (*nsock_on_data)(int fd) = NULL;
|
||||
void (*nsock_on_close)(int fd) = NULL;
|
||||
void nsock_on_before_data(int fd);
|
||||
|
||||
nsock_t *nsock_get(int fd) {
|
||||
if (nsock_socks[fd] == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (fd >= nsocks_count || nsocks[fd] == NULL) {
|
||||
if (fd >= nsocks_count) {
|
||||
nsocks_count = fd + 1;
|
||||
nsocks = (nsock_t **)realloc(nsocks, sizeof(nsock_t *) * sizeof(nsock_t) * (nsocks_count));
|
||||
nsocks[fd] = (nsock_t *)calloc(1, sizeof(nsock_t));
|
||||
}
|
||||
nsocks[fd]->upstreams = NULL;
|
||||
nsocks[fd]->fd = fd;
|
||||
nsocks[fd]->connected = false;
|
||||
nsocks[fd]->downstream = false;
|
||||
nsocks[fd]->upstream_count = 0;
|
||||
nsocks[fd]->type = NSOCK_CLIENT;
|
||||
return nsocks[fd];
|
||||
}
|
||||
return nsocks[fd];
|
||||
}
|
||||
|
||||
void nsock_close(int fd) {
|
||||
if (nsock_on_close)
|
||||
nsock_on_close(fd);
|
||||
nsock_t *sock = nsock_get(fd);
|
||||
if (sock && sock->connected) {
|
||||
sock->connected = false;
|
||||
for (unsigned int i = 0; i < sock->upstream_count; i++) {
|
||||
nsock_t *upstream = nsock_get(sock->upstreams[i]);
|
||||
if (upstream->connected)
|
||||
nsock_close(sock->upstreams[i]);
|
||||
sock->upstreams[i] = 0;
|
||||
}
|
||||
if (sock->upstream_count) {
|
||||
free(sock->upstreams);
|
||||
}
|
||||
sock->upstream_count = 0;
|
||||
}
|
||||
nsock_socks[fd] = 0;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
nsock_t *nsock_create(int fd, nsock_type_t type) {
|
||||
if (fd <= 0)
|
||||
return NULL;
|
||||
nsock_socks[fd] = fd;
|
||||
nsock_t *sock = nsock_get(fd);
|
||||
sock->connected = true;
|
||||
sock->downstream = false;
|
||||
sock->type = type;
|
||||
return sock;
|
||||
}
|
||||
|
||||
int *nsock_init(int socket_count) {
|
||||
if (nsock_socks) {
|
||||
return nsock_socks;
|
||||
}
|
||||
nsock_socks = (int *)calloc(1, sizeof(int) * sizeof(int *) * socket_count + 1);
|
||||
if (nsock_data) {
|
||||
free(nsock_data);
|
||||
nsock_data = NULL;
|
||||
}
|
||||
nsock_data = (void **)malloc(sizeof(void **) * socket_count + 1);
|
||||
nsock_socks[socket_count] = -1;
|
||||
return nsock_socks;
|
||||
}
|
||||
|
||||
void nsock_free() {
|
||||
if (nsock_socks)
|
||||
free(nsock_socks);
|
||||
if (nsock_readable)
|
||||
free(nsock_readable);
|
||||
nsock_server_fd = 0;
|
||||
nsock_max_socket_fd = 0;
|
||||
if (nsock_data) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void nsock_add_upstream(int source, int target, bool downstream) {
|
||||
if (!nsock_socks[target])
|
||||
return;
|
||||
if (!nsock_socks[source])
|
||||
return;
|
||||
nsock_t *sock = nsock_get(source);
|
||||
nsock_t *sock_target = nsock_get(target);
|
||||
sock_target->type = NSOCK_UPSTREAM;
|
||||
sock->upstreams = (int *)realloc(sock->upstreams, sizeof(int) * (sock->upstream_count + 1));
|
||||
sock->downstream = downstream;
|
||||
sock->upstreams[sock->upstream_count] = target;
|
||||
sock->upstream_count++;
|
||||
}
|
||||
|
||||
void *nsock_get_data(int socket) { return nsock_data[socket]; }
|
||||
void nsock_set_data(int socket, void *data) { nsock_data[socket] = data; }
|
||||
|
||||
int nsock_connect(const char *host, unsigned int port) {
|
||||
char port_str[10] = {0};
|
||||
sprintf(port_str, "%d", port);
|
||||
int status;
|
||||
int socket_fd = 0;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res;
|
||||
struct addrinfo *p;
|
||||
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
return false;
|
||||
}
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if ((status = getaddrinfo(host, port_str, &hints, &res)) != 0) {
|
||||
return 0;
|
||||
}
|
||||
for (p = res; p != NULL; p = p->ai_next) {
|
||||
if ((socket_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
|
||||
continue;
|
||||
}
|
||||
if (connect(socket_fd, p->ai_addr, p->ai_addrlen) == -1) {
|
||||
close(socket_fd);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (p == NULL) {
|
||||
freeaddrinfo(res);
|
||||
return 0;
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
if (socket_fd) {
|
||||
if (nsock_socks == NULL) {
|
||||
nsock_init(2048);
|
||||
}
|
||||
nsock_socks[socket_fd] = socket_fd;
|
||||
nsock_t *sock = nsock_create(socket_fd, NSOCK_CLIENT);
|
||||
sock->connected = true;
|
||||
}
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
void nsock_listen(int port) {
|
||||
int server_fd;
|
||||
struct sockaddr_in address;
|
||||
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||
perror("Socket failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
int opt = 1;
|
||||
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
|
||||
perror("setsockopt failed");
|
||||
close(server_fd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(port);
|
||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
perror("Bind failed");
|
||||
close(server_fd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (listen(server_fd, 8096) < 0) {
|
||||
perror("Listen failed");
|
||||
close(server_fd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
nsock_server_fd = server_fd;
|
||||
}
|
||||
|
||||
int *nsock_select(suseconds_t timeout) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeout;
|
||||
int server_fd = nsock_server_fd;
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(server_fd, &rfds);
|
||||
int *socks = nsock_socks;
|
||||
fd_set efds;
|
||||
FD_ZERO(&efds);
|
||||
nsock_max_socket_fd = server_fd;
|
||||
for (int i = 0; socks[i] != -1; i++) {
|
||||
if (i == server_fd)
|
||||
continue;
|
||||
;
|
||||
if (!socks[i])
|
||||
continue;
|
||||
if (socks[i] > nsock_max_socket_fd) {
|
||||
nsock_max_socket_fd = socks[i];
|
||||
}
|
||||
FD_SET(socks[i], &rfds);
|
||||
FD_SET(socks[i], &efds);
|
||||
}
|
||||
int activity = select(nsock_max_socket_fd + 1, &rfds, NULL, &efds, timeout == 0 ? NULL : &tv);
|
||||
if ((activity < 0) && (errno != EINTR)) {
|
||||
perror("Select error\n");
|
||||
return NULL;
|
||||
} else if (activity == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (FD_ISSET(server_fd, &rfds)) {
|
||||
struct sockaddr_in address;
|
||||
int addrlen = sizeof(address);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
int new_socket = 0;
|
||||
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
|
||||
perror("Accept failed");
|
||||
} else {
|
||||
nsock_socks[new_socket] = new_socket;
|
||||
nsock_create(new_socket, NSOCK_CLIENT);
|
||||
if (nsock_on_connect)
|
||||
nsock_on_connect(new_socket);
|
||||
if (new_socket > nsock_max_socket_fd)
|
||||
nsock_max_socket_fd = new_socket;
|
||||
}
|
||||
}
|
||||
if (nsock_readable) {
|
||||
free(nsock_readable);
|
||||
}
|
||||
nsock_readable = (int *)calloc(1, sizeof(int *) + sizeof(int) * (nsock_max_socket_fd + 2));
|
||||
nsock_readable[nsock_max_socket_fd + 1] = -1;
|
||||
nsock_readable[0] = 0;
|
||||
int readable_count = 0;
|
||||
for (int i = 0; i < nsock_max_socket_fd + 1; i++) {
|
||||
nsock_t *sock = nsock_get(i);
|
||||
if (!sock)
|
||||
continue;
|
||||
if (FD_ISSET(i, &efds)) {
|
||||
nsock_close(nsock_socks[i]);
|
||||
nsock_socks[i] = 0;
|
||||
nsock_readable[i] = 0;
|
||||
} else if (FD_ISSET(i, &rfds) && i != server_fd) {
|
||||
nsock_readable[i] = i;
|
||||
readable_count++;
|
||||
nsock_on_before_data(i);
|
||||
} else {
|
||||
nsock_readable[i] = 0;
|
||||
sock->connected = false;
|
||||
}
|
||||
}
|
||||
return nsock_readable;
|
||||
}
|
||||
|
||||
unsigned char *nsock_read(int fd, int length) {
|
||||
if (!nsock_socks[fd])
|
||||
return NULL;
|
||||
unsigned char *buffer = (unsigned char *)malloc(length + 1);
|
||||
int bytes_read = read(fd, buffer, length);
|
||||
if (bytes_read <= 0) {
|
||||
nsock_close(fd);
|
||||
return NULL;
|
||||
}
|
||||
buffer[bytes_read] = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned char *nsock_read_all(int fd, int length) {
|
||||
if (!nsock_socks[fd])
|
||||
return NULL;
|
||||
unsigned char *buffer = (unsigned char *)malloc(length + 1);
|
||||
int bytes_read = 0;
|
||||
while (bytes_read < length) {
|
||||
int bytes_chunk = read(fd, buffer + bytes_read, length - bytes_read);
|
||||
if (bytes_chunk <= 0) {
|
||||
nsock_close(fd);
|
||||
return NULL;
|
||||
}
|
||||
bytes_read += bytes_chunk;
|
||||
}
|
||||
buffer[bytes_read] = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int nsock_write_all(int fd, unsigned char *data, int length) {
|
||||
if (!nsock_socks[fd])
|
||||
return 0;
|
||||
int bytes_written = 0;
|
||||
while (bytes_written < length) {
|
||||
int bytes_chunk = write(fd, data + bytes_written, length - bytes_written);
|
||||
if (bytes_chunk <= 0) {
|
||||
nsock_close(fd);
|
||||
return 0;
|
||||
}
|
||||
bytes_written += bytes_chunk;
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int nsock_execute_upstream(int source, size_t buffer_size) {
|
||||
int result = 0;
|
||||
nsock_t *sock = nsock_get(source);
|
||||
unsigned char data[buffer_size];
|
||||
memset(data, 0, buffer_size);
|
||||
int bytes_read = read(source, data, buffer_size);
|
||||
if (bytes_read <= 0) {
|
||||
nsock_close(source);
|
||||
return 0;
|
||||
}
|
||||
bool downstreamed = false;
|
||||
for (unsigned int i = 0; i < sock->upstream_count; i++) {
|
||||
if (!nsock_socks[sock->upstreams[i]])
|
||||
continue;
|
||||
int bytes_sent = nsock_write_all(sock->upstreams[i], data, bytes_read);
|
||||
if (bytes_sent <= 0) {
|
||||
nsock_close(sock->upstreams[i]);
|
||||
continue;
|
||||
}
|
||||
if (sock->downstream && downstreamed == false) {
|
||||
downstreamed = true;
|
||||
unsigned char data[4096];
|
||||
memset(data, 0, 4096);
|
||||
int bytes_read = read(sock->upstreams[i], data, 4096);
|
||||
if (bytes_read <= 0) {
|
||||
nsock_close(source);
|
||||
return 0;
|
||||
}
|
||||
int bytes_sent = nsock_write_all(sock->fd, data, bytes_read);
|
||||
if (bytes_sent <= 0) {
|
||||
nsock_close(sock->upstreams[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nsock_on_before_data(int fd) {
|
||||
if (!nsock_socks[fd])
|
||||
return;
|
||||
nsock_t *sock = nsock_get(fd);
|
||||
if (sock->upstream_count) {
|
||||
int upstreamed_to_count = nsock_execute_upstream(fd, 4096);
|
||||
if (!upstreamed_to_count) {
|
||||
nsock_close(fd);
|
||||
}
|
||||
return;
|
||||
} else if (sock->type == NSOCK_UPSTREAM) {
|
||||
while (rfd_wait(sock->fd, 0)) {
|
||||
unsigned char *data = nsock_read(fd, 4096);
|
||||
(void)data;
|
||||
}
|
||||
}
|
||||
if (nsock_on_data)
|
||||
nsock_on_data(fd);
|
||||
}
|
||||
|
||||
void nsock(int port, void (*on_connect)(int fd), void (*on_data)(int fd), void (*on_close)(int fd)) {
|
||||
nsock_init(2048);
|
||||
nsock_listen(port);
|
||||
nsock_on_connect = on_connect;
|
||||
nsock_on_data = on_data;
|
||||
nsock_on_close = on_close;
|
||||
int serve_in_terminal = nsock_on_connect == NULL && nsock_on_data == NULL && nsock_on_close == NULL;
|
||||
while (1) {
|
||||
int *readable = nsock_select(0);
|
||||
if (!serve_in_terminal)
|
||||
continue;
|
||||
if (!readable)
|
||||
continue;
|
||||
for (int i = 0; readable[i] != -1; i++) {
|
||||
if (!readable[i])
|
||||
continue;
|
||||
char buffer[1024] = {0};
|
||||
int bytes_read = read(readable[i], buffer, 1);
|
||||
buffer[bytes_read] = 0;
|
||||
if (bytes_read <= 0) {
|
||||
nsock_close(readable[i]);
|
||||
continue;
|
||||
}
|
||||
if (write(readable[i], buffer, bytes_read) <= 0) {
|
||||
nsock_close(readable[i]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
20
r.c
Normal file
20
r.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include "rlib.h"
|
||||
|
||||
void dummy_function() {
|
||||
for (long i = 0; i < 100000; i++) {
|
||||
long a = i * 2;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
rbench_t *r = rbench_new();
|
||||
r->add_function(r, "function", "dummy_function", dummy_function);
|
||||
r->execute(r, 10000);
|
||||
|
||||
rbench_free(r);
|
||||
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
|
||||
// rprintr("\\l\\T message\n");
|
||||
}
|
||||
}
|
58
rargs.h
Normal file
58
rargs.h
Normal file
@ -0,0 +1,58 @@
|
||||
#include <stdio.h>
|
||||
#ifndef RLIB_RARGS_H
|
||||
#define RLIB_RARGS_H
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool rargs_isset(int argc, char *argv[], char *key) {
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char *rargs_get_option_string(int argc, char *argv[], char *key, const char *def) {
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], key)) {
|
||||
if (i < argc - 1) {
|
||||
return argv[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return (char *)def;
|
||||
}
|
||||
|
||||
int rargs_get_option_int(int argc, char *argv[], char *key, int def) {
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], key)) {
|
||||
if (i < argc - 1) {
|
||||
return atoi(argv[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
bool rargs_get_option_bool(int argc, char *argv[], char *key, bool def) {
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], key)) {
|
||||
if (i < argc - 1) {
|
||||
if (!strcmp(argv[i + 1], "false"))
|
||||
return false;
|
||||
if (!strcmp(argv[i + 1], "0"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
#endif
|
32
rautocomplete.c
Normal file
32
rautocomplete.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include "rautocomplete.h"
|
||||
|
||||
int main() {
|
||||
|
||||
rautocomplete_t *ac = rautocomplete_new();
|
||||
rstring_list_add(ac, "first");
|
||||
rstring_list_add(ac, "test2");
|
||||
rstring_list_add(ac, "test3");
|
||||
rstring_list_add(ac, "test4");
|
||||
rstring_list_add(ac, "test5");
|
||||
rstring_list_add(ac, "test6");
|
||||
rstring_list_add(ac, "test7");
|
||||
rstring_list_add(ac, "test8");
|
||||
rstring_list_add(ac, "test9");
|
||||
rstring_list_add(ac, "test10");
|
||||
rstring_list_add(ac, "test11");
|
||||
rstring_list_add(ac, "test12");
|
||||
rstring_list_add(ac, "test13");
|
||||
rstring_list_add(ac, "test14");
|
||||
rstring_list_add(ac, "test15");
|
||||
rstring_list_add(ac, "test16");
|
||||
rstring_list_add(ac, "test17");
|
||||
rstring_list_add(ac, "test18");
|
||||
rstring_list_add(ac, "test19");
|
||||
rstring_list_add(ac, "test20");
|
||||
printf(r4_escape("test"));
|
||||
char *str = rautocomplete_find(ac, "firsta");
|
||||
if (str)
|
||||
printf("%s\n", str);
|
||||
rautocomplete_free(ac);
|
||||
return 0;
|
||||
}
|
63
rautocomplete.h
Normal file
63
rautocomplete.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef RAUTOCOMPLETE_H
|
||||
#define RAUTOCOMPLETE_H
|
||||
#define R4_DEBUG
|
||||
#include "rrex4.h"
|
||||
#include "rstring_list.h"
|
||||
#define rautocomplete_new rstring_list_new
|
||||
#define rautocomplete_free rstring_list_free
|
||||
#define rautocomplete_add rstring_list_add
|
||||
#define rautocomplete_find rstring_list_find
|
||||
#define rautocomplete_t rstring_list_t
|
||||
#define rautocomplete_contains rstring_list_contains
|
||||
|
||||
char *r4_escape(char *content) {
|
||||
size_t size = strlen(content) * 2 + 1;
|
||||
char *escaped = (char *)calloc(size, sizeof(char));
|
||||
char *espr = escaped;
|
||||
char *to_escape = "?*+()[]{}^$\\";
|
||||
*espr = '(';
|
||||
espr++;
|
||||
while (*content) {
|
||||
if (strchr(to_escape, *content)) {
|
||||
*espr = '\\';
|
||||
espr++;
|
||||
}
|
||||
*espr = *content;
|
||||
espr++;
|
||||
content++;
|
||||
}
|
||||
*espr = '.';
|
||||
espr++;
|
||||
*espr = '+';
|
||||
espr++;
|
||||
*espr = ')';
|
||||
espr++;
|
||||
*espr = 0;
|
||||
return escaped;
|
||||
}
|
||||
|
||||
char *rautocomplete_find(rstring_list_t *list, char *expr) {
|
||||
if (!list->count)
|
||||
return NULL;
|
||||
if (!expr || !strlen(expr))
|
||||
return NULL;
|
||||
|
||||
char *escaped = r4_escape(expr);
|
||||
|
||||
for (unsigned int i = list->count - 1; i == 0; i--) {
|
||||
char *match;
|
||||
r4_t *r = r4(list->strings[i], escaped);
|
||||
if (r->valid && r->match_count == 1) {
|
||||
match = strdup(r->matches[0]);
|
||||
}
|
||||
r4_free(r);
|
||||
if (match) {
|
||||
|
||||
free(escaped);
|
||||
return match;
|
||||
}
|
||||
}
|
||||
free(escaped);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
388
rbench.c
Normal file
388
rbench.c
Normal file
@ -0,0 +1,388 @@
|
||||
#include "rbench.h"
|
||||
#include "rtest.h"
|
||||
#include "rtree.h"
|
||||
#include "rhashtable.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "rtime.h"
|
||||
|
||||
char *format_number_retoor(long lnumber) {
|
||||
static char formatted[1024];
|
||||
char number[1024];
|
||||
number[0] = 0;
|
||||
sprintf(number, "%ld", lnumber);
|
||||
size_t len = strlen(number);
|
||||
int comma_count = len / 3;
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
int i;
|
||||
formatted[comma_count + len] = 0;
|
||||
for (i = len + comma_count; i > 0; i--) {
|
||||
formatted[i - offset] = number[i - comma_count];
|
||||
if (count == 3) {
|
||||
count = 0;
|
||||
offset++;
|
||||
if (i > 1) {
|
||||
formatted[i - offset] = '.';
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
char *format_number_yurii(long long num) {
|
||||
static char buf[1024];
|
||||
char *buff = buf;
|
||||
int isneg = num < 0;
|
||||
if (isneg)
|
||||
num = -num;
|
||||
long long rev = num;
|
||||
size_t count;
|
||||
for (count = 0; num; count++, num /= 10)
|
||||
rev = rev * 10 + num % 10;
|
||||
count += (count - 1) / 3;
|
||||
|
||||
if (isneg)
|
||||
*buff++ = '-';
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if ((count - i) % 4 == 0) {
|
||||
*buff++ = '.';
|
||||
} else {
|
||||
*buff++ = (rev % 10 + '0');
|
||||
rev /= 10;
|
||||
}
|
||||
}
|
||||
*buff = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *format_number_gpt(long lnumber) {
|
||||
static char formatted[1024];
|
||||
|
||||
char number[1024];
|
||||
sprintf(number, "%ld", lnumber);
|
||||
|
||||
int len = strlen(number);
|
||||
int commas_needed = (len - 1) / 3; // Determine how many dots are needed
|
||||
int new_len = len + commas_needed; // New length with dots included
|
||||
|
||||
formatted[new_len] = '\0'; // Null-terminate the formatted string
|
||||
|
||||
int i = len - 1; // Index for original number
|
||||
int j = new_len - 1; // Index for formatted number
|
||||
int count = 0; // Counter for placing dots
|
||||
|
||||
while (i >= 0) {
|
||||
if (count == 3) {
|
||||
formatted[j--] = '.'; // Insert dot after every 3 digits
|
||||
count = 0; // Reset the counter
|
||||
}
|
||||
formatted[j--] = number[i--]; // Copy digit from the original number
|
||||
count++;
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
int rstrcmp(char *l, char *r) {
|
||||
while (*l && *l == *r) {
|
||||
l++;
|
||||
r++;
|
||||
}
|
||||
return *l - *r;
|
||||
}
|
||||
int strcmp_gpt(const char *str1, const char *str2) {
|
||||
while (*str1 && (*str1 == *str2)) {
|
||||
str1++;
|
||||
str2++;
|
||||
}
|
||||
|
||||
return *(unsigned char *)str1 - *(unsigned char *)str2;
|
||||
}
|
||||
int strcmp_clib(p1, p2) const char *p1;
|
||||
const char *p2;
|
||||
{
|
||||
register const unsigned char *s1 = (const unsigned char *)p1;
|
||||
register const unsigned char *s2 = (const unsigned char *)p2;
|
||||
unsigned c1, c2;
|
||||
|
||||
do {
|
||||
c1 = (unsigned char)*s1++;
|
||||
c2 = (unsigned char)*s2++;
|
||||
if (c1 == '\0')
|
||||
return c1 - c2;
|
||||
} while (c1 == c2);
|
||||
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
void bench_rstrcmp(void *arg1, void *arg2) { __attribute__((unused)) int res = rstrcmp(arg1, arg2); }
|
||||
void bench_cstrcmp(void *arg1, void *arg2) { __attribute__((unused)) int res = strcmp(arg1, arg2); }
|
||||
|
||||
bool bench_starts_with_r(const char *s1, const char *s2) { return rstrstartswith(s1, s2); }
|
||||
bool bench_ends_with_r(const char *s1, const char *s2) { return rstrendswith(s1, s2); }
|
||||
|
||||
bool bench_starts_with_gpt(const char *str, const char *prefix) {
|
||||
while (*prefix) {
|
||||
if (*str != *prefix) {
|
||||
return false; // Mismatch found
|
||||
}
|
||||
str++;
|
||||
prefix++;
|
||||
}
|
||||
return true; // All characters matched
|
||||
}
|
||||
|
||||
int bench_starts_with_yurii(const char *str, const char *start) {
|
||||
if (str == NULL)
|
||||
return start == NULL;
|
||||
if (str == start || start == NULL || *start == '\0')
|
||||
return 1;
|
||||
|
||||
return strncmp(str, start, strlen(start)) == 0;
|
||||
}
|
||||
|
||||
bool bench_ends_with_gpt(const char *str, const char *suffix) {
|
||||
size_t str_len = strlen(str);
|
||||
size_t suffix_len = strlen(suffix);
|
||||
|
||||
// If the suffix is longer than the string, it can't be a suffix
|
||||
if (suffix_len > str_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start comparing from the end of both strings
|
||||
const char *str_end = str + str_len - suffix_len;
|
||||
while (*suffix) {
|
||||
if (*str_end != *suffix) {
|
||||
return false; // Mismatch found
|
||||
}
|
||||
str_end++;
|
||||
suffix++;
|
||||
}
|
||||
|
||||
return true; // All characters matched
|
||||
}
|
||||
|
||||
int bench_ends_with_yurii(const char *str, const char *end) {
|
||||
size_t end_len;
|
||||
|
||||
if (str == NULL)
|
||||
return end == NULL;
|
||||
if (str == end || end == NULL || *end == '\0')
|
||||
return 1;
|
||||
|
||||
end_len = strlen(end);
|
||||
return strncmp(str + (strlen(str) - end_len), end, end_len) == 0;
|
||||
}
|
||||
|
||||
void plus(int v1, int v2) { __attribute__((unused)) int v3 = v1 + v2; }
|
||||
void min(int v1, int v2) { __attribute__((unused)) int v3 = v2 - v1; }
|
||||
|
||||
void bench_rstrmove_r() {
|
||||
char to_move_1[] = "abc?defgaa";
|
||||
rstrmove2(to_move_1, 3, 5, 0);
|
||||
rasserts(!strcmp(to_move_1, "?defgabcaa"));
|
||||
char to_move_2[] = "?defgabcaa";
|
||||
rstrmove2(to_move_2, 0, 5, 3);
|
||||
rasserts(!strcmp(to_move_2, "abc?defgaa"));
|
||||
char to_move_3[] = "?defgabcaa";
|
||||
rstrmove2(to_move_3, 0, 5, 6);
|
||||
rasserts(!strcmp(to_move_3, "abcaa?defg"));
|
||||
}
|
||||
|
||||
void bench_rstrmove_gpt() {
|
||||
char to_move_1[] = "abc?defgaa";
|
||||
rstrmove(to_move_1, 3, 5, 0);
|
||||
rasserts(!strcmp(to_move_1, "?defgabcaa"));
|
||||
char to_move_2[] = "?defgabcaa";
|
||||
rstrmove(to_move_2, 0, 5, 2);
|
||||
// printf("BECAME: %s\n",to_move_2);
|
||||
// Goes wrong!
|
||||
// rasserts(!strcmp(to_move_2, "ab?defgcaa"));
|
||||
char to_move_3[] = "?defgabcaa";
|
||||
rstrmove(to_move_3, 0, 5, 7);
|
||||
rasserts(!strcmp(to_move_3, "abc?defgaa"));
|
||||
}
|
||||
|
||||
void rbench_table_rtree() {
|
||||
rtree_t *tree = (rtree_t *)rbf->data;
|
||||
if (rbf->first) {
|
||||
tree = rtree_new();
|
||||
rbf->data = (void *)tree;
|
||||
}
|
||||
for (int i = 0; i < 1; i++) {
|
||||
char *key = rgenerate_key();
|
||||
rtree_set(tree, key, key);
|
||||
rasserts(!strcmp(rtree_get(tree, key), key));
|
||||
}
|
||||
if (rbf->last)
|
||||
rtree_free(rbf->data);
|
||||
}
|
||||
|
||||
void rbench_table_rhashtable() {
|
||||
for (int i = 0; i < 1; i++) {
|
||||
char *key = rgenerate_key();
|
||||
rset(key, key);
|
||||
rasserts(!strcmp(rget(key), key));
|
||||
}
|
||||
}
|
||||
|
||||
nsecs_t total_execution_time = 0;
|
||||
long total_times = 0;
|
||||
bool show_progress = 1;
|
||||
void bench_format_number(long times, long number) {
|
||||
rbench_t *r;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r = rbench_new();
|
||||
r->show_progress = show_progress;
|
||||
r->add_function(r, "number_format", "retoor", format_number_retoor);
|
||||
r->add_function(r, "number_format", "yurii", format_number_yurii);
|
||||
r->add_function(r, "number_format", "gpt", format_number_gpt);
|
||||
r->execute1(r, times, (void *)number);
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
|
||||
void bench_table(long times) {
|
||||
rbench_t *r;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r = rbench_new();
|
||||
r->show_progress = show_progress;
|
||||
r->add_function(r, "rtree", "retoor", rbench_table_rtree);
|
||||
r->add_function(r, "hashtable", "k*r", rbench_table_rhashtable);
|
||||
r->execute(r, times);
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
|
||||
void bench_rstrmove(long times) {
|
||||
rbench_t *r;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r = rbench_new();
|
||||
r->show_progress = show_progress;
|
||||
r->add_function(r, "rstrmove2", "retoor", bench_rstrmove_r);
|
||||
r->add_function(r, "rstrmove", "gpt", bench_rstrmove_gpt);
|
||||
r->execute(r, times);
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
void bench_math(long times) {
|
||||
rbench_t *r = rbench_new();
|
||||
r->show_progress = show_progress;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r->add_function(r, "plus", "math", plus);
|
||||
r->add_function(r, "min", "math", min);
|
||||
r->execute2(r, times, (void *)5, (void *)5);
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
void bench_strcmp(long times) {
|
||||
rbench_t *r = rbench_new();
|
||||
r->stdout = false;
|
||||
r->show_progress = show_progress;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r->add_function(r, "strcmp_clib", "scmp", strcmp_clib);
|
||||
r->add_function(r, "strcmp", "scmp", strcmp);
|
||||
r->add_function(r, "rstrcmp", "scmp", rstrcmp);
|
||||
r->add_function(r, "strcmp_gpt", "scmp", strcmp_gpt);
|
||||
r->execute2(r, times, "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz");
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
|
||||
void printf_strcat() {
|
||||
char buffer[1000] = {0};
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
strcat(buffer, "a");
|
||||
}
|
||||
printf("%s", buffer);
|
||||
}
|
||||
void printf_raw() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
printf("%s", "a");
|
||||
}
|
||||
}
|
||||
|
||||
void bench_sprintf(long times) {
|
||||
rbench_t *r = rbench_new();
|
||||
r->stdout = false;
|
||||
r->show_progress = show_progress;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r->add_function(r, "strcat", "buffered", printf_strcat);
|
||||
r->add_function(r, "printf", "raw", printf_raw);
|
||||
r->execute(r, times);
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
|
||||
void bench_startswith(long times) {
|
||||
rbench_t *r = rbench_new();
|
||||
r->stdout = false;
|
||||
r->show_progress = show_progress;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r->add_function(r, "startswith", "retoor", bench_starts_with_r);
|
||||
r->add_function(r, "startswith", "gpt", bench_starts_with_gpt);
|
||||
r->add_function(r, "startswith", "yurii", bench_starts_with_yurii);
|
||||
r->execute2(r, times, "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnop");
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
|
||||
void bench_endswith(long times) {
|
||||
rbench_t *r = rbench_new();
|
||||
r->stdout = false;
|
||||
r->show_progress = show_progress;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r->add_function(r, "endswith", "retoor", bench_ends_with_r);
|
||||
r->add_function(r, "endswith", "gpt", bench_ends_with_gpt);
|
||||
r->add_function(r, "endswith", "yurii", bench_ends_with_yurii);
|
||||
r->execute2(r, times, "abcdefghijklmnopqrstuvwxyzdef", "qrstuvwxyzdef");
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
|
||||
#define ifwhile(cond, action) \
|
||||
{ \
|
||||
bool _did_doit = false; \
|
||||
while (cond) { \
|
||||
_did_doit = true; \
|
||||
{ action } \
|
||||
} \
|
||||
if (_did_doit)
|
||||
|
||||
#define endifwhile }
|
||||
|
||||
int main() {
|
||||
show_progress = true;
|
||||
long times = 900000000;
|
||||
|
||||
printf("With %% progress times:\n");
|
||||
BENCH(times, { bench_starts_with_yurii("abcdefghijklmnopqrstuvw", "abcdef"); });
|
||||
BENCH(times, { bench_ends_with_yurii("abcdefghijklmnopqrstuvw", "uvw"); });
|
||||
|
||||
printf("Without %% progress times:\n");
|
||||
BENCH(times * 1000, { bench_starts_with_yurii("abcdefghijklmnopqrstuvw", "abcdef"); });
|
||||
BENCH(times * 1000, { bench_ends_with_yurii("abcdefghijklmnopqrstuvw", "uvw"); });
|
||||
|
||||
bench_table(times / 10000);
|
||||
bench_sprintf(times / 10000);
|
||||
bench_format_number(times / 100, 123456789);
|
||||
bench_rstrmove(times / 100);
|
||||
bench_math(times);
|
||||
bench_strcmp(times / 100);
|
||||
|
||||
bench_startswith(times / 10);
|
||||
bench_endswith(times / 10);
|
||||
printf("\nTotal execution time:%s\n", format_time(total_execution_time));
|
||||
printf("Total times: %s\n", rformat_number(total_times));
|
||||
|
||||
return 0;
|
||||
}
|
251
rbench.cpp
Normal file
251
rbench.cpp
Normal file
@ -0,0 +1,251 @@
|
||||
#include "rbench.h"
|
||||
#include "rprint.h"
|
||||
#include "rtree.h"
|
||||
#include "rhashtable.h"
|
||||
#include "rkeytable.h"
|
||||
#include "rtest.h"
|
||||
|
||||
#include "hashmap.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
long long hashit(const char **key) {
|
||||
long long hash = 0;
|
||||
const char *k = *key;
|
||||
for (; *k; k++) {
|
||||
hash += *k * 31 + 5;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
long long hashit2(const char **s) {
|
||||
unsigned hashval;
|
||||
const char *key = *s;
|
||||
for (hashval = 0; *key != '\0'; key++)
|
||||
hashval = *key + 31 * hashval;
|
||||
return hashval % HASHSIZE;
|
||||
}
|
||||
|
||||
bool keycmp(const char **key1, const char **key2) { return !strcmp(*key1, *key2); }
|
||||
|
||||
HashMap(HashMap_name, char *, char *, long long, hashit, keycmp)
|
||||
|
||||
void hashmap_print(const HashMap_name *map) {
|
||||
printf("Hash Map:\n");
|
||||
printf("\tBuckets: [");
|
||||
for (size_t i = 0; i < map->cap; i++) {
|
||||
if (i != 0)
|
||||
printf(",");
|
||||
printf(" %ld", map->buckets[i]);
|
||||
}
|
||||
printf(" ]\n");
|
||||
|
||||
printf("\tEntries: [");
|
||||
for (size_t i = 0; i < map->cap; i++) {
|
||||
HashMap_name_entry_t *entry = map->entries + i;
|
||||
if (i != 0)
|
||||
printf(", ");
|
||||
printf("{\n");
|
||||
printf("\t\tIndex: %zd,\n", i);
|
||||
printf("\t\tKey: \"%s\",\n", entry->key);
|
||||
printf("\t\tHash: %lld,\n", entry->key_hash);
|
||||
printf("\t\tNext: %ld,\n", entry->next);
|
||||
printf("\t\tVal: %s,\n", entry->val);
|
||||
printf("\t}");
|
||||
}
|
||||
printf("]\n");
|
||||
printf("\tNext Empty: %zd\n", map->next_empty);
|
||||
printf("\tSize: %zd\n", map->size);
|
||||
printf("\tCapacity: %zd\n", map->cap);
|
||||
}
|
||||
|
||||
void rbench_table_yuriimap() {
|
||||
HashMap_name *map = (HashMap_name *)rbf->data;
|
||||
if (rbf->first) {
|
||||
_r_generate_key_current = 0;
|
||||
HashMap_name *new_map = HashMap_name_new();
|
||||
HashMap_name_init(new_map, 0);
|
||||
rbf->data = (void *)new_map;
|
||||
map = new_map;
|
||||
}
|
||||
char *key = strdup(rgenerate_key());
|
||||
DS_codes_t code = HashMap_name_put(map, (const char **)&key, (const char **)&key);
|
||||
const char *res = *HashMap_name_get(map, (const char **)&key);
|
||||
rasserts(!strcmp(res, key));
|
||||
if (rbf->last) {
|
||||
HashMap_name_destroy(map);
|
||||
}
|
||||
}
|
||||
|
||||
void rbench_table_cppmap() {
|
||||
std::map<char *, char *> *map = (std::map<char *, char *> *)rbf->data;
|
||||
if (rbf->first) {
|
||||
_r_generate_key_current = 0;
|
||||
std::map<char *, char *> *f = new std::map<char *, char *>;
|
||||
map = f;
|
||||
rbf->data = (void *)f;
|
||||
}
|
||||
for (int i = 0; i < 1; i++) {
|
||||
char *key = strdup(rgenerate_key());
|
||||
(*map)[key] = key;
|
||||
rasserts(!strcmp((*map)[key], key));
|
||||
}
|
||||
|
||||
if (rbf->last) {
|
||||
delete map;
|
||||
}
|
||||
}
|
||||
|
||||
void rbench_table_rtree() {
|
||||
rtree_t *tree = (rtree_t *)rbf->data;
|
||||
if (rbf->first) {
|
||||
_r_generate_key_current = 0;
|
||||
tree = rtree_new();
|
||||
rbf->data = (void *)tree;
|
||||
}
|
||||
for (int i = 0; i < 1; i++) {
|
||||
char *key = rgenerate_key();
|
||||
rtree_set(tree, key, key);
|
||||
rasserts(!strcmp((char *)rtree_get(tree, key), key));
|
||||
}
|
||||
if (rbf->last)
|
||||
rtree_free((rtree_t *)rbf->data);
|
||||
}
|
||||
|
||||
void rbench_table_rhashtable() {
|
||||
if (rbf->first)
|
||||
_r_generate_key_current = 0;
|
||||
for (int i = 0; i < 1; i++) {
|
||||
char *key = rgenerate_key();
|
||||
rset(key, key);
|
||||
rasserts(!strcmp(rget(key), key));
|
||||
}
|
||||
}
|
||||
|
||||
void rbench_table_rkeytable() {
|
||||
if (rbf->first)
|
||||
_r_generate_key_current = 0;
|
||||
for (int i = 0; i < 1; i++) {
|
||||
char *key = rgenerate_key();
|
||||
rkset(key, key);
|
||||
rasserts(!strcmp(rkget(key), key));
|
||||
}
|
||||
}
|
||||
|
||||
nsecs_t total_execution_time = 0;
|
||||
long total_times = 0;
|
||||
bool show_progress = 1;
|
||||
|
||||
void bench_fast_yurrii() {
|
||||
HashMap_name *map = HashMap_name_new();
|
||||
HashMap_name_init(map, 0);
|
||||
char *key = NULL;
|
||||
BENCHP(90000, {
|
||||
key = rgenerate_key();
|
||||
DS_codes_t code = HashMap_name_put(map, (const char **)&key, (const char **)&key);
|
||||
const char *res = *HashMap_name_get(map, (const char **)&key);
|
||||
rasserts(!strcmp(res, key));
|
||||
});
|
||||
HashMap_name_destroy(map);
|
||||
}
|
||||
void bench_fast_retoor() {
|
||||
rtree_t *tree = rtree_new();
|
||||
char *key = NULL;
|
||||
BENCHP(90000, {
|
||||
key = rgenerate_key();
|
||||
rtree_set(tree, key, key);
|
||||
rasserts(!strcmp((char *)rtree_get(tree, key), key));
|
||||
});
|
||||
rtree_free(tree);
|
||||
}
|
||||
|
||||
void bench_table(long times) {
|
||||
|
||||
bench_fast_retoor();
|
||||
bench_fast_yurrii();
|
||||
rbench_t *r;
|
||||
rprint("\\T B\\l Times: %ld\n", times);
|
||||
r = rbench_new();
|
||||
r->show_progress = show_progress;
|
||||
r->add_function(r, "hashmap", "yurii", rbench_table_yuriimap);
|
||||
r->add_function(r, "rtree", "retoor", rbench_table_rtree);
|
||||
r->add_function(r, "hashtable", "k&r", rbench_table_rhashtable);
|
||||
// r->add_function(r, "keytable", "k*r", rbench_table_rkeytable);
|
||||
r->add_function(r, "map", "cpp", rbench_table_cppmap);
|
||||
r->execute(r, times);
|
||||
total_execution_time += r->execution_time;
|
||||
total_times += times * 2;
|
||||
rbench_free(r);
|
||||
}
|
||||
|
||||
typedef struct rtree2_t {
|
||||
struct rtree2_t *children[255];
|
||||
struct rtree2_t *next[255];
|
||||
void *value;
|
||||
char c;
|
||||
} rtree2_t;
|
||||
|
||||
rtree2_t *rtree2_new() {
|
||||
rtree2_t *tree = (rtree2_t *)calloc(1, sizeof(rtree2_t));
|
||||
return tree;
|
||||
}
|
||||
|
||||
void rtree2_set(rtree2_t *tree, char *key, void *value) {
|
||||
if (tree->c == *key) {
|
||||
key++;
|
||||
if (!key) {
|
||||
tree->value = value;
|
||||
}
|
||||
} else {
|
||||
if (tree->children[*key]) {
|
||||
tree = tree->children[*key];
|
||||
} else {
|
||||
printf("HIER EERS %cT\n", *key);
|
||||
tree = tree->children[*key] = rtree2_new();
|
||||
tree->c = *key;
|
||||
}
|
||||
key++;
|
||||
if (!*key) {
|
||||
printf("HIEr LAatSt 5c\n", *key);
|
||||
tree->value = value;
|
||||
return;
|
||||
}
|
||||
rtree2_set(tree, key, value);
|
||||
}
|
||||
}
|
||||
void *rtree2_get(rtree2_t *tree, char *key) {
|
||||
while (*key) {
|
||||
tree = tree->children[*key];
|
||||
key++;
|
||||
}
|
||||
return tree->value;
|
||||
}
|
||||
|
||||
void bench_fast_retoor2() {
|
||||
rtree2_t *tree = rtree2_new();
|
||||
char *key = NULL;
|
||||
BENCHP(90000, {
|
||||
key = rgenerate_key();
|
||||
rtree2_set(tree, key, key);
|
||||
rasserts(!strcmp((char *)rtree2_get(tree, key), key));
|
||||
});
|
||||
// rtree_free(tree);
|
||||
}
|
||||
|
||||
int main() {
|
||||
rtree2_t *tree = rtree2_new();
|
||||
|
||||
char name[] = "key";
|
||||
rtree2_set(tree, name, (void *)strdup("lala"));
|
||||
char *val = (char *)rtree2_get(tree, name);
|
||||
printf("%s\n", val);
|
||||
bench_fast_retoor2();
|
||||
exit(0);
|
||||
show_progress = true;
|
||||
long times = 90000;
|
||||
bench_table(times);
|
||||
|
||||
return rtest_end("");
|
||||
}
|
370
rbench.h
Normal file
370
rbench.h
Normal file
@ -0,0 +1,370 @@
|
||||
#ifndef RBENCH_H
|
||||
#define RBENCH_H
|
||||
|
||||
#include "rprint.h"
|
||||
#include "rtime.h"
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "rstring.h"
|
||||
#include "rterminal.h"
|
||||
|
||||
#define RBENCH(times, action) \
|
||||
{ \
|
||||
unsigned long utimes = (unsigned long)times; \
|
||||
nsecs_t start = nsecs(); \
|
||||
for (unsigned long i = 0; i < utimes; i++) { \
|
||||
{ \
|
||||
action; \
|
||||
} \
|
||||
} \
|
||||
nsecs_t end = nsecs(); \
|
||||
printf("%s\n", format_time(end - start)); \
|
||||
}
|
||||
|
||||
#define RBENCHP(times, action) \
|
||||
{ \
|
||||
printf("\n"); \
|
||||
nsecs_t start = nsecs(); \
|
||||
unsigned int prev_percentage = 0; \
|
||||
unsigned long utimes = (unsigned long)times; \
|
||||
for (unsigned long i = 0; i < utimes; i++) { \
|
||||
unsigned int percentage = ((long double)i / (long double)times) * 100; \
|
||||
int percentage_changed = percentage != prev_percentage; \
|
||||
__attribute__((unused)) int first = i == 0; \
|
||||
__attribute__((unused)) int last = i == utimes - 1; \
|
||||
{ action; }; \
|
||||
if (percentage_changed) { \
|
||||
printf("\r%d%%", percentage); \
|
||||
fflush(stdout); \
|
||||
\
|
||||
prev_percentage = percentage; \
|
||||
} \
|
||||
} \
|
||||
nsecs_t end = nsecs(); \
|
||||
printf("\r%s\n", format_time(end - start)); \
|
||||
}
|
||||
|
||||
struct rbench_t;
|
||||
|
||||
typedef struct rbench_function_t {
|
||||
#ifdef __cplusplus
|
||||
void (*call)();
|
||||
#else
|
||||
void(*call);
|
||||
#endif
|
||||
char name[256];
|
||||
char group[256];
|
||||
void *arg;
|
||||
void *data;
|
||||
bool first;
|
||||
bool last;
|
||||
int argc;
|
||||
unsigned long times_executed;
|
||||
|
||||
nsecs_t average_execution_time;
|
||||
nsecs_t total_execution_time;
|
||||
} rbench_function_t;
|
||||
|
||||
typedef struct rbench_t {
|
||||
unsigned int function_count;
|
||||
rbench_function_t functions[100];
|
||||
rbench_function_t *current;
|
||||
rprogressbar_t *progress_bar;
|
||||
bool show_progress;
|
||||
int winner;
|
||||
bool stdout;
|
||||
unsigned long times;
|
||||
bool silent;
|
||||
nsecs_t execution_time;
|
||||
#ifdef __cplusplus
|
||||
void (*add_function)(struct rbench_t *r, const char *name, const char *group, void (*)());
|
||||
#else
|
||||
void (*add_function)(struct rbench_t *r, const char *name, const char *group, void *);
|
||||
#endif
|
||||
void (*rbench_reset)(struct rbench_t *r);
|
||||
struct rbench_t *(*execute)(struct rbench_t *r, long times);
|
||||
struct rbench_t *(*execute1)(struct rbench_t *r, long times, void *arg1);
|
||||
struct rbench_t *(*execute2)(struct rbench_t *r, long times, void *arg1, void *arg2);
|
||||
struct rbench_t *(*execute3)(struct rbench_t *r, long times, void *arg1, void *arg2, void *arg3);
|
||||
|
||||
} rbench_t;
|
||||
|
||||
FILE *_rbench_stdout = NULL;
|
||||
FILE *_rbench_stdnull = NULL;
|
||||
|
||||
void rbench_toggle_stdout(rbench_t *r) {
|
||||
if (!r->stdout) {
|
||||
if (_rbench_stdout == NULL) {
|
||||
_rbench_stdout = stdout;
|
||||
}
|
||||
if (_rbench_stdnull == NULL) {
|
||||
_rbench_stdnull = fopen("/dev/null", "wb");
|
||||
}
|
||||
if (stdout == _rbench_stdout) {
|
||||
stdout = _rbench_stdnull;
|
||||
} else {
|
||||
stdout = _rbench_stdout;
|
||||
}
|
||||
}
|
||||
}
|
||||
void rbench_restore_stdout(rbench_t *r) {
|
||||
if (r->stdout)
|
||||
return;
|
||||
if (_rbench_stdout) {
|
||||
stdout = _rbench_stdout;
|
||||
}
|
||||
if (_rbench_stdnull) {
|
||||
fclose(_rbench_stdnull);
|
||||
_rbench_stdnull = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
rbench_t *rbench_new();
|
||||
|
||||
rbench_t *_rbench = NULL;
|
||||
rbench_function_t *rbf;
|
||||
rbench_t *rbench() {
|
||||
if (_rbench == NULL) {
|
||||
_rbench = rbench_new();
|
||||
}
|
||||
return _rbench;
|
||||
}
|
||||
|
||||
typedef void *(*rbench_call)();
|
||||
typedef void *(*rbench_call1)(void *);
|
||||
typedef void *(*rbench_call2)(void *, void *);
|
||||
typedef void *(*rbench_call3)(void *, void *, void *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
void rbench_add_function(rbench_t *rp, const char *name, const char *group, void (*call)()) {
|
||||
#else
|
||||
void rbench_add_function(rbench_t *rp, const char *name, const char *group, void *call) {
|
||||
#endif
|
||||
rbench_function_t *f = &rp->functions[rp->function_count];
|
||||
rp->function_count++;
|
||||
f->average_execution_time = 0;
|
||||
f->total_execution_time = 0;
|
||||
f->times_executed = 0;
|
||||
f->call = call;
|
||||
strcpy(f->name, name);
|
||||
strcpy(f->group, group);
|
||||
}
|
||||
|
||||
void rbench_reset_function(rbench_function_t *f) {
|
||||
f->average_execution_time = 0;
|
||||
f->times_executed = 0;
|
||||
f->total_execution_time = 0;
|
||||
}
|
||||
|
||||
void rbench_reset(rbench_t *rp) {
|
||||
for (unsigned int i = 0; i < rp->function_count; i++) {
|
||||
rbench_reset_function(&rp->functions[i]);
|
||||
}
|
||||
}
|
||||
int rbench_get_winner_index(rbench_t *r) {
|
||||
int winner = 0;
|
||||
nsecs_t time = 0;
|
||||
for (unsigned int i = 0; i < r->function_count; i++) {
|
||||
if (time == 0 || r->functions[i].total_execution_time < time) {
|
||||
winner = i;
|
||||
time = r->functions[i].total_execution_time;
|
||||
}
|
||||
}
|
||||
return winner;
|
||||
}
|
||||
bool rbench_was_last_function(rbench_t *r) {
|
||||
for (unsigned int i = 0; i < r->function_count; i++) {
|
||||
if (i == r->function_count - 1 && r->current == &r->functions[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
rbench_function_t *rbench_execute_prepare(rbench_t *r, int findex, long times, int argc) {
|
||||
rbench_toggle_stdout(r);
|
||||
if (findex == 0) {
|
||||
r->execution_time = 0;
|
||||
}
|
||||
rbench_function_t *rf = &r->functions[findex];
|
||||
rf->argc = argc;
|
||||
rbf = rf;
|
||||
r->current = rf;
|
||||
if (r->show_progress)
|
||||
r->progress_bar = rprogressbar_new(0, times, 20, stderr);
|
||||
r->times = times;
|
||||
// printf(" %s:%s gets executed for %ld times with %d
|
||||
// arguments.\n",rf->group, rf->name, times,argc);
|
||||
rbench_reset_function(rf);
|
||||
|
||||
return rf;
|
||||
}
|
||||
void rbench_execute_finish(rbench_t *r) {
|
||||
rbench_toggle_stdout(r);
|
||||
if (r->progress_bar) {
|
||||
free(r->progress_bar);
|
||||
r->progress_bar = NULL;
|
||||
}
|
||||
r->current->average_execution_time = r->current->total_execution_time / r->current->times_executed;
|
||||
;
|
||||
// printf(" %s:%s finished executing in
|
||||
// %s\n",r->current->group,r->current->name,
|
||||
// format_time(r->current->total_execution_time));
|
||||
// rbench_show_results_function(r->current);
|
||||
if (rbench_was_last_function(r)) {
|
||||
rbench_restore_stdout(r);
|
||||
unsigned int winner_index = rbench_get_winner_index(r);
|
||||
r->winner = winner_index + 1;
|
||||
if (!r->silent)
|
||||
rprintgf(stderr, "Benchmark results:\n");
|
||||
nsecs_t total_time = 0;
|
||||
|
||||
for (unsigned int i = 0; i < r->function_count; i++) {
|
||||
rbf = &r->functions[i];
|
||||
total_time += rbf->total_execution_time;
|
||||
bool is_winner = winner_index == i;
|
||||
if (is_winner) {
|
||||
if (!r->silent)
|
||||
rprintyf(stderr, " > %s:%s:%s\n", format_time(rbf->total_execution_time), rbf->group, rbf->name);
|
||||
} else {
|
||||
if (!r->silent)
|
||||
rprintbf(stderr, " %s:%s:%s\n", format_time(rbf->total_execution_time), rbf->group, rbf->name);
|
||||
}
|
||||
}
|
||||
if (!r->silent)
|
||||
rprintgf(stderr, "Total execution time: %s\n", format_time(total_time));
|
||||
}
|
||||
rbench_restore_stdout(r);
|
||||
rbf = NULL;
|
||||
r->current = NULL;
|
||||
}
|
||||
struct rbench_t *rbench_execute(rbench_t *r, long times) {
|
||||
|
||||
for (unsigned int i = 0; i < r->function_count; i++) {
|
||||
|
||||
rbench_function_t *f = rbench_execute_prepare(r, i, times, 0);
|
||||
rbench_call c = (rbench_call)f->call;
|
||||
nsecs_t start = nsecs();
|
||||
f->first = true;
|
||||
c();
|
||||
f->first = false;
|
||||
f->last = false;
|
||||
f->times_executed++;
|
||||
for (int j = 1; j < times; j++) {
|
||||
c();
|
||||
f->times_executed++;
|
||||
f->last = f->times_executed == r->times - 1;
|
||||
if (r->progress_bar) {
|
||||
rprogressbar_update(r->progress_bar, f->times_executed);
|
||||
}
|
||||
}
|
||||
f->total_execution_time = nsecs() - start;
|
||||
r->execution_time += f->total_execution_time;
|
||||
rbench_execute_finish(r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
struct rbench_t *rbench_execute1(rbench_t *r, long times, void *arg1) {
|
||||
|
||||
for (unsigned int i = 0; i < r->function_count; i++) {
|
||||
rbench_function_t *f = rbench_execute_prepare(r, i, times, 1);
|
||||
rbench_call1 c = (rbench_call1)f->call;
|
||||
nsecs_t start = nsecs();
|
||||
f->first = true;
|
||||
c(arg1);
|
||||
f->first = false;
|
||||
f->last = false;
|
||||
f->times_executed++;
|
||||
for (int j = 1; j < times; j++) {
|
||||
c(arg1);
|
||||
f->times_executed++;
|
||||
f->last = f->times_executed == r->times - 1;
|
||||
if (r->progress_bar) {
|
||||
rprogressbar_update(r->progress_bar, f->times_executed);
|
||||
}
|
||||
}
|
||||
f->total_execution_time = nsecs() - start;
|
||||
r->execution_time += f->total_execution_time;
|
||||
rbench_execute_finish(r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
struct rbench_t *rbench_execute2(rbench_t *r, long times, void *arg1, void *arg2) {
|
||||
|
||||
for (unsigned int i = 0; i < r->function_count; i++) {
|
||||
rbench_function_t *f = rbench_execute_prepare(r, i, times, 2);
|
||||
rbench_call2 c = (rbench_call2)f->call;
|
||||
nsecs_t start = nsecs();
|
||||
f->first = true;
|
||||
c(arg1, arg2);
|
||||
f->first = false;
|
||||
f->last = false;
|
||||
f->times_executed++;
|
||||
for (int j = 1; j < times; j++) {
|
||||
c(arg1, arg2);
|
||||
f->times_executed++;
|
||||
f->last = f->times_executed == r->times - 1;
|
||||
if (r->progress_bar) {
|
||||
rprogressbar_update(r->progress_bar, f->times_executed);
|
||||
}
|
||||
}
|
||||
f->total_execution_time = nsecs() - start;
|
||||
r->execution_time += f->total_execution_time;
|
||||
rbench_execute_finish(r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
struct rbench_t *rbench_execute3(rbench_t *r, long times, void *arg1, void *arg2, void *arg3) {
|
||||
|
||||
for (unsigned int i = 0; i < r->function_count; i++) {
|
||||
rbench_function_t *f = rbench_execute_prepare(r, i, times, 3);
|
||||
|
||||
rbench_call3 c = (rbench_call3)f->call;
|
||||
nsecs_t start = nsecs();
|
||||
f->first = true;
|
||||
c(arg1, arg2, arg3);
|
||||
f->first = false;
|
||||
f->last = false;
|
||||
f->times_executed++;
|
||||
for (int j = 1; j < times; j++) {
|
||||
c(arg1, arg2, arg3);
|
||||
f->times_executed++;
|
||||
f->last = f->times_executed == r->times - 1;
|
||||
if (r->progress_bar) {
|
||||
rprogressbar_update(r->progress_bar, f->times_executed);
|
||||
}
|
||||
}
|
||||
f->total_execution_time = nsecs() - start;
|
||||
rbench_execute_finish(r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
rbench_t *rbench_new() {
|
||||
|
||||
rbench_t *r = (rbench_t *)malloc(sizeof(rbench_t));
|
||||
memset(r, 0, sizeof(rbench_t));
|
||||
r->add_function = rbench_add_function;
|
||||
r->rbench_reset = rbench_reset;
|
||||
r->execute1 = rbench_execute1;
|
||||
r->execute2 = rbench_execute2;
|
||||
r->execute3 = rbench_execute3;
|
||||
r->execute = rbench_execute;
|
||||
r->stdout = true;
|
||||
r->silent = false;
|
||||
r->winner = 0;
|
||||
r->show_progress = true;
|
||||
return r;
|
||||
}
|
||||
void rbench_free(rbench_t *r) { free(r); }
|
||||
|
||||
#endif
|
39
rbuffer.c
Normal file
39
rbuffer.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include "rtest.h"
|
||||
#include "rbuffer.h"
|
||||
|
||||
int main() {
|
||||
rtest_banner("rbuffer");
|
||||
unsigned char *content = (unsigned char *)"[ {\n\t \"\r1\t3\n4truefalsetrue \" }, ]";
|
||||
char *ignore = "\r| |\n|\t|\f|\v";
|
||||
rbuffer_t *buffer = rbuffer_new(content, ustrlen(content));
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '[');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '{');
|
||||
rbuffer_reset(buffer);
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '[');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '{');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '"');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '1');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '3');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '4');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == 't');
|
||||
rbuffer_pop(buffer);
|
||||
rbuffer_pop(buffer);
|
||||
rbuffer_pop(buffer);
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == 'f');
|
||||
rbuffer_pop(buffer);
|
||||
rbuffer_pop(buffer);
|
||||
rbuffer_pop(buffer);
|
||||
rbuffer_pop(buffer);
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == 't');
|
||||
rbuffer_pop(buffer);
|
||||
rbuffer_pop(buffer);
|
||||
rbuffer_pop(buffer);
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == '"');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b|}", ignore) == '}');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b", ignore) == ',');
|
||||
assert(*rbuffer_consume(buffer, "{|[|,|\"|\\d|\\b|]", ignore) == ']');
|
||||
unsigned char *str = rbuffer_to_string(buffer);
|
||||
printf(">%s<\n", str);
|
||||
free(str);
|
||||
return rtest_end("");
|
||||
}
|
174
rbuffer.h
Normal file
174
rbuffer.h
Normal file
@ -0,0 +1,174 @@
|
||||
#ifndef RBUFFER_H
|
||||
#define RBUFFER_H
|
||||
#include "rmalloc.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
typedef struct rbuffer_t {
|
||||
unsigned char *data;
|
||||
unsigned char *_data;
|
||||
size_t size;
|
||||
size_t pos;
|
||||
bool eof;
|
||||
} rbuffer_t;
|
||||
|
||||
rbuffer_t *rbuffer_new(unsigned char *data, size_t size);
|
||||
void rbuffer_free(rbuffer_t *rfb);
|
||||
void rbuffer_reset(rbuffer_t *rfb);
|
||||
void rbuffer_write(rbuffer_t *rfb, const unsigned char *data, size_t size);
|
||||
size_t rbuffer_push(rbuffer_t *rfb, unsigned char);
|
||||
unsigned char rbuffer_pop(rbuffer_t *rfb);
|
||||
unsigned char *rbuffer_expect(rbuffer_t *rfb, char *options, char *ignore);
|
||||
void rbuffer_set(rbuffer_t *rfb, const unsigned char *data, size_t size);
|
||||
|
||||
void rbuffer_set(rbuffer_t *rfb, const unsigned char *data, size_t size) {
|
||||
if (rfb->_data) {
|
||||
free(rfb->_data);
|
||||
rfb->_data = NULL;
|
||||
rfb->data = NULL;
|
||||
rfb->eof = true;
|
||||
}
|
||||
if (size) {
|
||||
rfb->_data = (unsigned char *)malloc(size);
|
||||
memcpy(rfb->_data, data, size);
|
||||
rfb->data = rfb->_data;
|
||||
rfb->eof = false;
|
||||
}
|
||||
rfb->size = size;
|
||||
rfb->pos = 0;
|
||||
}
|
||||
|
||||
rbuffer_t *rbuffer_new(unsigned char *data, size_t size) {
|
||||
rbuffer_t *rfb = (rbuffer_t *)malloc(sizeof(rbuffer_t));
|
||||
if (size) {
|
||||
rfb->_data = (unsigned char *)malloc(size);
|
||||
memcpy(rfb->_data, data, size);
|
||||
rfb->eof = false;
|
||||
} else {
|
||||
rfb->_data = NULL;
|
||||
rfb->eof = true;
|
||||
}
|
||||
rfb->size = size;
|
||||
rfb->pos = 0;
|
||||
rfb->data = rfb->_data;
|
||||
return rfb;
|
||||
}
|
||||
void rbuffer_free(rbuffer_t *rfb) {
|
||||
if (rfb->_data)
|
||||
free(rfb->_data);
|
||||
free(rfb);
|
||||
}
|
||||
|
||||
size_t rbuffer_push(rbuffer_t *rfb, unsigned char c) {
|
||||
if (rfb->pos < rfb->size) {
|
||||
rfb->_data[rfb->pos++] = c;
|
||||
return 1;
|
||||
}
|
||||
rfb->_data = realloc(rfb->_data, rfb->size ? rfb->size + 1 : rfb->size + 2);
|
||||
rfb->_data[rfb->pos++] = c;
|
||||
rfb->size++;
|
||||
return rfb->pos;
|
||||
}
|
||||
void rbuffer_write(rbuffer_t *rfb, const unsigned char *data, size_t size) {
|
||||
unsigned char *data_ptr = (unsigned char *)data;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
rbuffer_push(rfb, data_ptr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char rbuffer_peek(rbuffer_t *rfb) {
|
||||
unsigned char result = EOF;
|
||||
if (rfb->pos != rfb->size) {
|
||||
result = rfb->_data[rfb->pos];
|
||||
return result;
|
||||
}
|
||||
rfb->eof = true;
|
||||
return EOF;
|
||||
}
|
||||
unsigned char rbuffer_pop(rbuffer_t *rfb) {
|
||||
unsigned char result = EOF;
|
||||
if (rfb->pos <= rfb->size) {
|
||||
result = rfb->_data[rfb->pos];
|
||||
rfb->pos++;
|
||||
rfb->data++;
|
||||
if (rfb->pos == rfb->size) {
|
||||
rfb->eof = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
rfb->eof = true;
|
||||
return result;
|
||||
}
|
||||
void rbuffer_reset(rbuffer_t *rfb) {
|
||||
rfb->data = rfb->_data;
|
||||
rfb->pos = 0;
|
||||
}
|
||||
|
||||
unsigned char ustrncmp(const unsigned char *s1, const unsigned char *s2, size_t n) {
|
||||
return strncmp((char *)s1, (char *)s2, n);
|
||||
while (n && *s1 == *s2) {
|
||||
n--;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return *s1 != *s2;
|
||||
}
|
||||
size_t ustrlen(const unsigned char *s) { return strlen((char *)s); }
|
||||
|
||||
unsigned char *rbuffer_to_string(rbuffer_t *rfb) {
|
||||
unsigned char *result = rfb->_data;
|
||||
rfb->_data = NULL;
|
||||
rfb->data = NULL;
|
||||
rbuffer_free(rfb);
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned char *rbuffer_match_option(rbuffer_t *rfb, char *options) {
|
||||
char *option = NULL;
|
||||
char options_cpy[1024] = {0};
|
||||
strcpy(options_cpy, options);
|
||||
char *memory = options_cpy;
|
||||
while ((option = strtok_r(option == NULL ? memory : NULL, "|", &memory)) != NULL) {
|
||||
|
||||
size_t option_length = strlen(option);
|
||||
if (option_length > rfb->size - rfb->pos) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(option, "\\d") && *rfb->data >= '0' && *rfb->data <= '9') {
|
||||
return rfb->data;
|
||||
}
|
||||
if (rfb->size - rfb->pos >= 5 && !strcmp(option, "\\b") &&
|
||||
((!ustrncmp(rfb->data, (unsigned char *)"true", 4) || !ustrncmp(rfb->data, (unsigned char *)"false", 5)))) {
|
||||
return rfb->data;
|
||||
}
|
||||
if (!ustrncmp(rfb->data, (unsigned char *)option, option_length)) {
|
||||
return rfb->data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char *rbuffer_expect(rbuffer_t *rfb, char *options, char *ignore) {
|
||||
while (rfb->pos < rfb->size) {
|
||||
if (rbuffer_match_option(rfb, options) != NULL) {
|
||||
return rfb->data;
|
||||
}
|
||||
if (rbuffer_match_option(rfb, ignore)) {
|
||||
printf("SKIP:%s\n", rfb->data);
|
||||
rbuffer_pop(rfb);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
unsigned char *rbuffer_consume(rbuffer_t *rfb, char *options, char *ignore) {
|
||||
unsigned char *result = NULL;
|
||||
if ((result = rbuffer_expect(rfb, options, ignore)) != NULL) {
|
||||
rbuffer_pop(rfb);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
3
rcase.c
Normal file
3
rcase.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include "rcase.h"
|
||||
|
||||
int main(int argc, char *argv[]) { return rcase_main(argc, argv); }
|
164
rcase.h
Normal file
164
rcase.h
Normal file
@ -0,0 +1,164 @@
|
||||
#ifndef RCASE_H
|
||||
#define RCASE_H
|
||||
#include "rio.h"
|
||||
#include "rmalloc.h"
|
||||
#include "rprint.h"
|
||||
#include "rstring.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define RCAMEL_CASE 1
|
||||
#define RSNAKE_CASE 2
|
||||
#define RINVALID_CASE 0
|
||||
#define RCONST_TEST_T 4;
|
||||
|
||||
int rdetermine_case(const char *str) {
|
||||
int length = strlen(str);
|
||||
|
||||
char p = 0;
|
||||
while (*str) {
|
||||
if (p == '_' && islower(*str))
|
||||
return RSNAKE_CASE;
|
||||
if (p != '_' && !isupper(p) && isupper(*str))
|
||||
return RCAMEL_CASE;
|
||||
p = *str;
|
||||
str++;
|
||||
}
|
||||
return RINVALID_CASE;
|
||||
|
||||
if (length == 0) {
|
||||
return RINVALID_CASE;
|
||||
}
|
||||
if (strchr(str, '_')) {
|
||||
if (str[0] == '_' || str[length - 1] == '_' || strstr(str, "__")) {
|
||||
return RINVALID_CASE;
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!islower(str[i]) && str[i] != '_') {
|
||||
return RINVALID_CASE;
|
||||
}
|
||||
}
|
||||
return RSNAKE_CASE;
|
||||
} else {
|
||||
|
||||
if (!islower(str[0])) {
|
||||
return RINVALID_CASE;
|
||||
}
|
||||
for (int i = 1; i < length; i++) {
|
||||
if (str[i] == '_') {
|
||||
return RINVALID_CASE;
|
||||
}
|
||||
if (isupper(str[i]) && isupper(str[i - 1])) {
|
||||
return RINVALID_CASE;
|
||||
}
|
||||
}
|
||||
return RCAMEL_CASE;
|
||||
}
|
||||
}
|
||||
|
||||
char *rsnake_to_camel(const char *snake_case) {
|
||||
int length = strlen(snake_case);
|
||||
char *camel_case = (char *)malloc(length + 1);
|
||||
int j = 0;
|
||||
int toUpper = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (i > 0 && snake_case[i] == '_' && snake_case[i + 1] == 'T') {
|
||||
toUpper = 1;
|
||||
if (snake_case[i + 1] == 'T' && (snake_case[i + 2] != '\n' || snake_case[i + 2] != '\0' || snake_case[i + 2] != ' ')) {
|
||||
|
||||
toUpper = 0;
|
||||
}
|
||||
}
|
||||
if (snake_case[i] == '_' && snake_case[i + 1] != 't') {
|
||||
toUpper = 1;
|
||||
if (snake_case[i + 1] == 't' && (snake_case[i + 2] != '\n' || snake_case[i + 2] != '\0' || snake_case[i + 2] != ' ')) {
|
||||
toUpper = 0;
|
||||
}
|
||||
} else if (snake_case[i] == '_' && snake_case[i + 1] == 't' && !isspace(snake_case[i + 2])) {
|
||||
toUpper = 1;
|
||||
} else if (snake_case[i] == '_' && snake_case[i + 1] == 'T' && !isspace(snake_case[i + 2])) {
|
||||
toUpper = 1;
|
||||
camel_case[j++] = '_';
|
||||
j++;
|
||||
} else {
|
||||
if (toUpper) {
|
||||
camel_case[j++] = toupper(snake_case[i]);
|
||||
toUpper = 0;
|
||||
} else {
|
||||
camel_case[j++] = snake_case[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
camel_case[j] = '\0';
|
||||
return camel_case;
|
||||
}
|
||||
char *rcamel_to_snake(const char *camelCase) {
|
||||
int length = strlen(camelCase);
|
||||
char *snake_case = (char *)malloc(2 * length + 1);
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (isupper(camelCase[i])) {
|
||||
if (i != 0) {
|
||||
snake_case[j++] = '_';
|
||||
}
|
||||
snake_case[j++] = tolower(camelCase[i]);
|
||||
} else {
|
||||
snake_case[j++] = camelCase[i];
|
||||
}
|
||||
}
|
||||
|
||||
snake_case[j] = '\0';
|
||||
return snake_case;
|
||||
}
|
||||
|
||||
char *rflip_case(char *content) {
|
||||
if (rdetermine_case(content) == RSNAKE_CASE) {
|
||||
return rcamel_to_snake(content);
|
||||
} else if (rdetermine_case(content) == RCAMEL_CASE) {
|
||||
return rsnake_to_camel(content);
|
||||
} else {
|
||||
rprintr("Could not determine case\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char *rflip_case_file(char *filepath) {
|
||||
size_t file_size = rfile_size(filepath);
|
||||
if (file_size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
char *content = (char *)malloc(file_size);
|
||||
char *result = NULL;
|
||||
if (rfile_readb(filepath, content, file_size)) {
|
||||
result = rflip_case(content);
|
||||
if (result) {
|
||||
free(content);
|
||||
return result;
|
||||
} else {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int rcase_main(int argc, char *argv[]) {
|
||||
if (argc < 2) {
|
||||
printf("usage: rcase <file>\n");
|
||||
return 1;
|
||||
}
|
||||
for (int i = 1; i < argc; i++) {
|
||||
char *result = rflip_case_file(argv[i]);
|
||||
if (result) {
|
||||
printf("%s\n", result);
|
||||
free(result);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
3
rcat.c
Normal file
3
rcat.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include "rcat.h"
|
||||
|
||||
int main(int argc, char *argv[]) { return rcat_main(argc, argv); }
|
29
rcat.h
Normal file
29
rcat.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef RCAT_H
|
||||
#define RCAT_H
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void rcat(char *filename) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (!f) {
|
||||
printf("rcat: couldn't open \"%s\" for read.\n", filename);
|
||||
return;
|
||||
}
|
||||
unsigned char c;
|
||||
while ((c = fgetc(f)) && !feof(f)) {
|
||||
printf("%c", c);
|
||||
}
|
||||
fclose(f);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
int rcat_main(int argc, char *argv[]) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: [filename]\n");
|
||||
return 1;
|
||||
}
|
||||
rcat(argv[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
3
rcov.c
Normal file
3
rcov.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include "rcov.h"
|
||||
|
||||
int main(int argc, char *argv[]) { return rcov_main(argc, argv); }
|
77
rcov.h
Normal file
77
rcov.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef RCOV_H
|
||||
#define RCOV_H
|
||||
#include "rtypes.h"
|
||||
#include "rtemp.h"
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "rbench.h"
|
||||
bool check_lcov() {
|
||||
char buffer[1024 * 64];
|
||||
FILE *fp;
|
||||
fp = popen("lcov --help", "r");
|
||||
if (fp == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (fgets(buffer, sizeof(buffer), fp) == NULL) {
|
||||
return false;
|
||||
}
|
||||
pclose(fp);
|
||||
return strstr(buffer, "lcov: not found") ? false : true;
|
||||
}
|
||||
|
||||
int rcov_main(int argc, char *argv[]) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: [source.c]\n");
|
||||
return 1;
|
||||
}
|
||||
char argstr[4096] = {0};
|
||||
for (int i = 2; i < argc; i++) {
|
||||
strcat(argstr, argv[i]);
|
||||
strcat(argstr, " ");
|
||||
}
|
||||
if (!check_lcov()) {
|
||||
|
||||
printf("lcov is not installed. Please execute `sudo apt install lcov`.\n");
|
||||
return 1;
|
||||
}
|
||||
char *source_file = argv[1];
|
||||
char *commands[] = {"rm -f *.gcda 2>/dev/null",
|
||||
"rm -f *.gcno 2>/dev/null",
|
||||
"rm -f %s.coverage.info 2>/dev/null",
|
||||
"gcc -pg -fprofile-arcs -ftest-coverage -g -o %s_coverage.o %s",
|
||||
"./%s_coverage.o",
|
||||
"lcov --capture --directory . --output-file %s.coverage.info",
|
||||
"genhtml %s.coverage.info --output-directory /tmp/%s.coverage",
|
||||
"rm -f *.gcda 2>/dev/null",
|
||||
"rm -f *.gcno 2>/dev/null",
|
||||
"rm -f %s.coverage.info 2>/dev/null", //"cat gmon.out",
|
||||
|
||||
"gprof %s_coverage.o gmon.out > output.rcov_analysis",
|
||||
|
||||
"rm -f gmon.out",
|
||||
"cat output.rcov_analysis",
|
||||
"rm output.rcov_analysis",
|
||||
"rm -f %s_coverage.o",
|
||||
|
||||
"google-chrome /tmp/%s.coverage/index.html"};
|
||||
uint command_count = sizeof(commands) / sizeof(commands[0]);
|
||||
RBENCH(1,{
|
||||
for (uint i = 0; i < command_count; i++) {
|
||||
char *formatted_command = sbuf("");
|
||||
sprintf(formatted_command, commands[i], source_file, source_file);
|
||||
// printf("%s\n", formatted_command);
|
||||
if (formatted_command[0] == '.' && formatted_command[1] == '/') {
|
||||
strcat(formatted_command, " ");
|
||||
strcat(formatted_command, argstr);
|
||||
}
|
||||
|
||||
if (system(formatted_command)) {
|
||||
printf("`%s` returned non-zero code.\n", formatted_command);
|
||||
}
|
||||
});
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
162
reditor.c
Normal file
162
reditor.c
Normal file
@ -0,0 +1,162 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
void rget_terminal_size(int *x, int *y) {
|
||||
struct winsize w;
|
||||
|
||||
// Get terminal size
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
// Print terminal size
|
||||
// printf("Rows: %d, Columns: %d\n", w.ws_row, w.ws_col);
|
||||
|
||||
// You can use this width in your program logic
|
||||
int terminal_width = w.ws_col;
|
||||
*x = w.ws_col;
|
||||
*y = w.ws_row;
|
||||
|
||||
// printf("Setting content width to half the terminal width:\n");
|
||||
|
||||
// Example content that fits within half the terminal width
|
||||
// printf("%.*s\n", terminal_width / 2, "This text is formatted to half
|
||||
// the terminal width.");
|
||||
}
|
||||
|
||||
struct termios rorig_termios;
|
||||
|
||||
// Restore original terminal settings
|
||||
void reset_terminal_mode() { tcsetattr(STDIN_FILENO, TCSANOW, &rorig_termios); }
|
||||
|
||||
void set_raw_mode() {
|
||||
struct termios new_termios;
|
||||
tcgetattr(STDIN_FILENO, &rorig_termios); // Get current terminal settings
|
||||
atexit(reset_terminal_mode); // Ensure original settings are restored on exit
|
||||
|
||||
new_termios = rorig_termios;
|
||||
new_termios.c_lflag &= ~(ICANON | ECHO); // Disable canonical mode and echoing
|
||||
new_termios.c_cc[VMIN] = 1; // Minimum number of characters for noncanonical read
|
||||
new_termios.c_cc[VTIME] = 0; // Timeout in deciseconds for noncanonical read
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &new_termios); // Apply new settings
|
||||
}
|
||||
|
||||
unsigned int read_key() {
|
||||
int nread;
|
||||
char c;
|
||||
if ((nread = read(STDIN_FILENO, &c, 1)) == -1)
|
||||
return -1;
|
||||
return c;
|
||||
}
|
||||
|
||||
void rrclear() {
|
||||
printf("\033[2J"); // Clear screen
|
||||
}
|
||||
|
||||
void rset_cursor_position(int x, int y) {
|
||||
// rrclear();
|
||||
printf("\033[%d;%dH", y, x);
|
||||
}
|
||||
|
||||
void get_cursor_position(int *cols, int *rows) {
|
||||
char buf[32];
|
||||
unsigned int i = 0;
|
||||
|
||||
// Request cursor position
|
||||
printf("\033[6n");
|
||||
|
||||
// Read the response: ESC [ rows ; cols R
|
||||
while (i < sizeof(buf) - 1) {
|
||||
if (read(STDIN_FILENO, buf + i, 1) != 1)
|
||||
break;
|
||||
if (buf[i] == 'R')
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
buf[i] = '\0';
|
||||
|
||||
// Parse the response
|
||||
if (buf[0] == '\033' && buf[1] == '[') {
|
||||
sscanf(buf + 2, "%d;%d", rows, cols);
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
int c;
|
||||
int x = 3;
|
||||
int y = 3; // Initial y position
|
||||
int file_index = 0;
|
||||
set_raw_mode();
|
||||
printf("\033[2J"); // Clear screen
|
||||
rset_cursor_position(x, y);
|
||||
char screen_data[1024];
|
||||
|
||||
int width, height;
|
||||
rget_terminal_size(&width, &height);
|
||||
|
||||
screen_data[0] = 0;
|
||||
for (int i = 0; i < width * height; i++) { // screen_data[i] = '\0';
|
||||
// screen_data[i] = 0;
|
||||
}
|
||||
memset(&screen_data, 0, 2048);
|
||||
// printf(screen_data);
|
||||
|
||||
while (1) {
|
||||
c = read_key();
|
||||
if (c == '\033') { // If the first character is ESC
|
||||
|
||||
if (read_key() == '[') { // If the second character is '['
|
||||
rrclear();
|
||||
c = read_key();
|
||||
if (c == 'A') {
|
||||
if (y) {
|
||||
y--;
|
||||
}
|
||||
rset_cursor_position(x, y);
|
||||
} else if (c == 'B') {
|
||||
if (y) {
|
||||
y++;
|
||||
}
|
||||
rset_cursor_position(x, y);
|
||||
} else if (c == 'C') {
|
||||
|
||||
x++;
|
||||
|
||||
rset_cursor_position(x, y);
|
||||
|
||||
} else if (c == 'D') {
|
||||
x--;
|
||||
|
||||
rset_cursor_position(x, y);
|
||||
}
|
||||
printf(screen_data);
|
||||
}
|
||||
} else if (c == 'q') {
|
||||
break; // Press 'q' to quit
|
||||
} else {
|
||||
for (int i = 0; i < file_index; i++) {
|
||||
if (screen_data[i] == '\0') {
|
||||
screen_data[i] = ' ';
|
||||
}
|
||||
}
|
||||
screen_data[file_index] = c;
|
||||
// file_index++;
|
||||
get_cursor_position(&x, &y);
|
||||
file_index = x * y;
|
||||
x++;
|
||||
// putc(c, stdout);
|
||||
// rrclear();
|
||||
rset_cursor_position(1, 1);
|
||||
// ss x++;
|
||||
printf(screen_data);
|
||||
rset_cursor_position(x, y);
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() { run(); }
|
7
remo.c
Normal file
7
remo.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include "remo.h"
|
||||
|
||||
int main() {
|
||||
remo_print();
|
||||
printf("<%s>", remo_get("zany"));
|
||||
return 0;
|
||||
}
|
247
remo.h
Normal file
247
remo.h
Normal file
@ -0,0 +1,247 @@
|
||||
#ifndef REMO_H
|
||||
#define REMO_H
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
const char *str;
|
||||
const char *description;
|
||||
} remo_t;
|
||||
|
||||
remo_t remo[] = {
|
||||
{"\U0001F600", "Grinning Face"}, // 😀
|
||||
{"\U0001F601", "Beaming Face with Smiling Eyes"}, // 😁
|
||||
{"\U0001F602", "Face with Tears of Joy"}, // 😂
|
||||
{"\U0001F923", "Rolling on the Floor Laughing"}, // 🤣
|
||||
{"\U0001F603", "Grinning Face with Big Eyes"}, // 😃
|
||||
{"\U0001F604", "Grinning Face with Smiling Eyes"}, // 😄
|
||||
{"\U0001F609", "Winking Face"}, // 😉
|
||||
{"\U0001F60A", "Smiling Face with Smiling Eyes"}, // 😊
|
||||
{"\U0001F60D", "Smiling Face with Heart-Eyes"}, // 😍
|
||||
{"\U0001F618", "Face Blowing a Kiss"}, // 😘
|
||||
{"\U0001F617", "Kissing Face"}, // 😗
|
||||
{"\U0001F61A", "Kissing Face with Closed Eyes"}, // 😚
|
||||
{"\U0001F642", "Slightly Smiling Face"}, // 🙂
|
||||
{"\U0001F643", "Upside-Down Face"}, // 🙃
|
||||
{"\U0001F970", "Smiling Face with Hearts"}, // 🥰
|
||||
{"\U0001F60B", "Face Savoring Food"}, // 😋
|
||||
{"\U0001F61B", "Face with Tongue"}, // 😛
|
||||
{"\U0001F61C", "Winking Face with Tongue"}, // 😜
|
||||
{"\U0001F92A", "Zany Face"}, // 🤪
|
||||
{"\U0001F929", "Star-Struck"}, // 🤩
|
||||
{"\U0001F631", "Face Screaming in Fear"}, // 😱
|
||||
{"\U0001F62D", "Loudly Crying Face"}, // 😭
|
||||
{"\U0001F624", "Face with Steam From Nose"}, // 😤
|
||||
{"\U0001F620", "Angry Face"}, // 😠
|
||||
{"\U0001F621", "Pouting Face"}, // 😡
|
||||
{"\U0001F47B", "Ghost"}, // 👻
|
||||
{"\U0001F480", "Skull"}, // 💀
|
||||
{"\U0001F4A9", "Pile of Poo"}, // 💩
|
||||
{"\U0001F47D", "Alien"}, // 👽
|
||||
// Geometric Shapes
|
||||
{"\U000025A0", "Black Square"}, // ■
|
||||
{"\U000025B2", "Upward Triangle"}, // ▲
|
||||
{"\U000025CF", "Black Circle"}, // ●
|
||||
{"\U000025CB", "White Circle"}, // ○
|
||||
{"\U00002B1B", "Large Black Square"}, // ⬛
|
||||
{"\U00002B1C", "Large White Square"}, // ⬜
|
||||
|
||||
// Mathematical Symbols
|
||||
{"\U00002200", "For All"}, // ∀
|
||||
{"\U00002203", "Exists"}, // ∃
|
||||
{"\U00002205", "Empty Set"}, // ∅
|
||||
{"\U00002207", "Nabla"}, // ∇
|
||||
{"\U0000220F", "N-Ary Product"}, // ∏
|
||||
{"\U00002212", "Minus Sign"}, // −
|
||||
{"\U0000221E", "Infinity"}, // ∞
|
||||
|
||||
// Arrows
|
||||
{"\U00002190", "Left Arrow"}, // ←
|
||||
{"\U00002191", "Up Arrow"}, // ↑
|
||||
{"\U00002192", "Right Arrow"}, // →
|
||||
{"\U00002193", "Down Arrow"}, // ↓
|
||||
{"\U00002195", "Up Down Arrow"}, // ↕
|
||||
{"\U00002197", "Up Right Arrow"}, // ↗
|
||||
{"\U00002198", "Down Right Arrow"}, // ↘
|
||||
{"\U000027A1", "Black Right Arrow"}, // ➡️
|
||||
|
||||
// Dingbats
|
||||
{"\U00002714", "Check Mark"}, // ✔️
|
||||
{"\U00002716", "Heavy Multiplication X"}, // ✖️
|
||||
{"\U00002728", "Sparkles"}, // ✨
|
||||
{"\U00002757", "Exclamation Mark"}, // ❗
|
||||
{"\U0000274C", "Cross Mark"}, // ❌
|
||||
{"\U00002795", "Heavy Plus Sign"}, // ➕
|
||||
|
||||
// Miscellaneous Symbols
|
||||
{"\U00002600", "Sun"}, // ☀️
|
||||
{"\U00002614", "Umbrella with Rain Drops"}, // ☔
|
||||
{"\U00002620", "Skull and Crossbones"}, // ☠️
|
||||
{"\U000026A0", "Warning Sign"}, // ⚠️
|
||||
{"\U000026BD", "Soccer Ball"}, // ⚽
|
||||
{"\U000026C4", "Snowman"}, // ⛄
|
||||
|
||||
// Stars and Asterisks
|
||||
{"\U00002733", "Eight Pointed Black Star"}, // ✳️
|
||||
{"\U00002734", "Eight Spoked Asterisk"}, // ✴️
|
||||
{"\U00002B50", "White Star"}, // ⭐
|
||||
{"\U0001F31F", "Glowing Star"}, // 🌟
|
||||
{"\U00002728", "Sparkles"}, // ✨
|
||||
// Animals and Nature
|
||||
{"\U0001F98A", "Fox"}, // 🦊
|
||||
{"\U0001F415", "Dog"}, // 🐕
|
||||
{"\U0001F431", "Cat Face"}, // 🐱
|
||||
{"\U0001F435", "Monkey Face"}, // 🐵
|
||||
{"\U0001F408", "Black Cat"}, // 🐈
|
||||
{"\U0001F98C", "Deer"}, // 🦌
|
||||
{"\U0001F344", "Mushroom"}, // 🍄
|
||||
{"\U0001F333", "Tree"}, // 🌳
|
||||
|
||||
// Weather and Space Symbols
|
||||
{"\U0001F308", "Rainbow"}, // 🌈
|
||||
{"\U0001F320", "Shooting Star"}, // 🌠
|
||||
{"\U00002600", "Sun"}, // ☀️
|
||||
{"\U00002601", "Cloud"}, // ☁️
|
||||
{"\U000026A1", "High Voltage"}, // ⚡
|
||||
{"\U0001F525", "Fire"}, // 🔥
|
||||
{"\U000026C4", "Snowman"}, // ⛄
|
||||
{"\U0001F30A", "Water Wave"}, // 🌊
|
||||
|
||||
// Transport and Map Symbols
|
||||
{"\U0001F68C", "Bus"}, // 🚌
|
||||
{"\U0001F697", "Car"}, // 🚗
|
||||
{"\U0001F6B2", "Bicycle"}, // 🚲
|
||||
{"\U0001F6A2", "Ship"}, // 🚢
|
||||
{"\U0001F681", "Helicopter"}, // 🚁
|
||||
{"\U0001F680", "Rocket"}, // 🚀
|
||||
{"\U0001F6EB", "Airplane"}, // 🛫
|
||||
|
||||
// Currency Symbols
|
||||
{"\U00000024", "Dollar Sign"}, // $
|
||||
{"\U000000A3", "Pound Sign"}, // £
|
||||
{"\U000000A5", "Yen Sign"}, // ¥
|
||||
{"\U000020AC", "Euro Sign"}, // €
|
||||
{"\U0001F4B5", "Dollar Banknote"}, // 💵
|
||||
{"\U0001F4B4", "Yen Banknote"}, // 💴
|
||||
|
||||
// Card Suits
|
||||
{"\U00002660", "Black Spade Suit"}, // ♠️
|
||||
{"\U00002663", "Black Club Suit"}, // ♣️
|
||||
{"\U00002665", "Black Heart Suit"}, // ♥️
|
||||
{"\U00002666", "Black Diamond Suit"}, // ♦️
|
||||
{"\U0001F0CF", "Joker Card"}, // 🃏
|
||||
|
||||
// Office Supplies and Objects
|
||||
{"\U0001F4DA", "Books"}, // 📚
|
||||
{"\U0001F4D7", "Green Book"}, // 📗
|
||||
{"\U0001F4C8", "Chart with Upwards Trend"}, // 📈
|
||||
{"\U0001F4C9", "Chart with Downwards Trend"}, // 📉
|
||||
{"\U0001F4B0", "Money Bag"}, // 💰
|
||||
{"\U0001F4B8", "Money with Wings"}, // 💸
|
||||
{"\U0001F4E6", "Package"}, // 📦
|
||||
|
||||
// Miscellaneous Symbols
|
||||
{"\U00002757", "Exclamation Mark"}, // ❗
|
||||
{"\U00002714", "Check Mark"}, // ✔️
|
||||
{"\U0000274C", "Cross Mark"}, // ❌
|
||||
{"\U00002705", "Check Mark Button"}, // ✅
|
||||
{"\U00002B50", "White Star"}, // ⭐
|
||||
{"\U0001F31F", "Glowing Star"}, // 🌟
|
||||
{"\U0001F4A1", "Light Bulb"}, // 💡
|
||||
{"\U0001F4A3", "Bomb"}, // 💣
|
||||
{"\U0001F4A9", "Pile of Poo"}, // 💩
|
||||
// Musical Symbols
|
||||
{"\U0001F3B5", "Musical Note"}, // 🎵
|
||||
{"\U0001F3B6", "Multiple Musical Notes"}, // 🎶
|
||||
{"\U0001F3BC", "Musical Score"}, // 🎼
|
||||
{"\U0001F399", "Studio Microphone"}, // 🎙️
|
||||
{"\U0001F3A4", "Microphone"}, // 🎤
|
||||
|
||||
// Food and Drink
|
||||
{"\U0001F35F", "Cheese Wedge"}, // 🧀
|
||||
{"\U0001F355", "Slice of Pizza"}, // 🍕
|
||||
{"\U0001F32D", "Taco"}, // 🌮
|
||||
{"\U0001F37D", "Beer Mug"}, // 🍻
|
||||
{"\U0001F96B", "Cup with Straw"}, // 🥤
|
||||
{"\U0001F32E", "Hot Pepper"}, // 🌶️
|
||||
{"\U0001F95A", "Potato"}, // 🥔
|
||||
|
||||
// Zodiac Signs
|
||||
{"\U00002600", "Aries"}, // ♈
|
||||
{"\U00002601", "Taurus"}, // ♉
|
||||
{"\U00002602", "Gemini"}, // ♊
|
||||
{"\U00002603", "Cancer"}, // ♋
|
||||
{"\U00002604", "Leo"}, // ♌
|
||||
{"\U00002605", "Virgo"}, // ♍
|
||||
{"\U00002606", "Libra"}, // ♎
|
||||
{"\U00002607", "Scorpio"}, // ♏
|
||||
{"\U00002608", "Sagittarius"}, // ♐
|
||||
{"\U00002609", "Capricorn"}, // ♑
|
||||
{"\U0000260A", "Aquarius"}, // ♒
|
||||
{"\U0000260B", "Pisces"}, // ♓
|
||||
|
||||
// Miscellaneous Shapes
|
||||
{"\U0001F4C8", "Chart Increasing"}, // 📈
|
||||
{"\U0001F4C9", "Chart Decreasing"}, // 📉
|
||||
{"\U0001F4CA", "Bar Chart"}, // 📊
|
||||
{"\U0001F7E6", "Orange Circle"}, // 🟠
|
||||
{"\U0001F7E7", "Yellow Circle"}, // 🟡
|
||||
{"\U0001F7E8", "Green Circle"}, // 🟢
|
||||
{"\U0001F7E9", "Blue Circle"}, // 🔵
|
||||
{"\U0001F7EA", "Purple Circle"}, // 🟣
|
||||
|
||||
// Flags
|
||||
{"\U0001F1E6\U0001F1E9", "Flag of France"}, // 🇫🇷
|
||||
{"\U0001F1E8\U0001F1E6", "Flag of Germany"}, // 🇩🇪
|
||||
{"\U0001F1FA\U0001F1F8", "Flag of United States"}, // 🇺🇸
|
||||
{"\U0001F1E7\U0001F1F7", "Flag of Canada"}, // 🇨🇦
|
||||
{"\U0001F1EE\U0001F1F2", "Flag of Italy"}, // 🇮🇹
|
||||
{"\U0001F1F8\U0001F1EC", "Flag of Australia"}, // 🇦🇺
|
||||
{"\U0001F1F3\U0001F1F4", "Flag of Spain"}, // 🇪🇸
|
||||
|
||||
// Additional Miscellaneous Symbols
|
||||
{"\U0001F4A5", "Collision"}, // 💥
|
||||
{"\U0001F4A6", "Sweat Droplets"}, // 💦
|
||||
{"\U0001F4A8", "Dashing Away"}, // 💨
|
||||
{"\U0001F50B", "Battery"}, // 🔋
|
||||
{"\U0001F4BB", "Laptop Computer"}, // 💻
|
||||
{"\U0001F4DE", "Telephone"}, // 📞
|
||||
{"\U0001F4E7", "Incoming Envelope"}, // 📧
|
||||
};
|
||||
size_t remo_count = sizeof(remo) / sizeof(remo[0]);
|
||||
|
||||
void rstrtolower(const char *input, char *output) {
|
||||
while (*input) {
|
||||
*output = tolower(*input);
|
||||
input++;
|
||||
output++;
|
||||
}
|
||||
*output = 0;
|
||||
}
|
||||
bool rstrinstr(const char *haystack, const char *needle) {
|
||||
char lower1[strlen(haystack) + 1];
|
||||
char lower2[strlen(needle) + 1];
|
||||
rstrtolower(haystack, lower1);
|
||||
rstrtolower(needle, lower2);
|
||||
return strstr(lower1, lower2) ? true : false;
|
||||
}
|
||||
|
||||
void remo_print() {
|
||||
|
||||
for (size_t i = 0; i < remo_count; i++) {
|
||||
printf("%s - %s\n", remo[i].str, remo[i].description);
|
||||
}
|
||||
}
|
||||
|
||||
const char *remo_get(char *name) {
|
||||
for (size_t i = 0; i < remo_count; i++) {
|
||||
if (rstrinstr(remo[i].description, name)) {
|
||||
return remo[i].str;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
1
resources/large.json
Normal file
1
resources/large.json
Normal file
File diff suppressed because one or more lines are too long
113
rfalloc.c
Normal file
113
rfalloc.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// r file memory
|
||||
typedef struct rfm_t {
|
||||
char path[4096];
|
||||
void *data;
|
||||
size_t size;
|
||||
long ptr;
|
||||
FILE *f;
|
||||
int fd;
|
||||
} rfm_t;
|
||||
|
||||
rfm_t rfm;
|
||||
bool _initialized = false;
|
||||
|
||||
void rfm_destroy(rfm_t *r) {
|
||||
if (munmap(r->data, r->size) == -1) {
|
||||
perror("munmap");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
close(rfm.fd);
|
||||
}
|
||||
|
||||
void *falloc(size_t s) {
|
||||
rfm_t *rr = &rfm;
|
||||
|
||||
printf("hier\n");
|
||||
char *data = (char *)rr->data + rfm.ptr;
|
||||
// data+= rfm.ptr;
|
||||
rfm.ptr += s;
|
||||
return data;
|
||||
}
|
||||
|
||||
void *finit(char *path, size_t size) {
|
||||
|
||||
printf("HIERR\n");
|
||||
if (!_initialized) {
|
||||
rfm.ptr = 0;
|
||||
_initialized = true;
|
||||
rfm.size = size;
|
||||
|
||||
printf("HIERR\n");
|
||||
memset(&rfm, 0, sizeof(rfm_t));
|
||||
rfm.size = size;
|
||||
rfm.ptr = 0;
|
||||
|
||||
printf("HIERR\n");
|
||||
// rfm.fd = open(path, O_RDWR);
|
||||
// ftruncate(rfm.fd, size);
|
||||
|
||||
if (path) {
|
||||
printf("HIERR\n");
|
||||
rfm.path[0] = 0;
|
||||
strcpy(rfm.path, path);
|
||||
// creat(path,F_)
|
||||
rfm.fd = open(path, O_RDWR);
|
||||
printf("OPEN %s\n", path);
|
||||
}
|
||||
if (!path) {
|
||||
rfm.f = tmpfile();
|
||||
rfm.fd = fileno(rfm.f);
|
||||
}
|
||||
// if (ftruncate(rfm.fd, size) == -1)
|
||||
// {
|
||||
// perror("ftruncate");
|
||||
// exit(EXIT_FAILURE);
|
||||
// }
|
||||
// rfensurefile(path,1024*1024);
|
||||
rfm.data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, rfm.fd, 0);
|
||||
printf("HIERR\n");
|
||||
}
|
||||
/*
|
||||
if (rfm.data == MAP_FAILED)
|
||||
{
|
||||
perror("mmap");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
char *data = (char *)rfm.data;
|
||||
printf(data);
|
||||
if (munmap(rfm.data, size) == -1)
|
||||
{
|
||||
perror("munmap");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fclose(rfm.f);
|
||||
*/
|
||||
}
|
||||
int main() {
|
||||
// Step 1: Create a temporary file
|
||||
finit("tast.dat", 1024 * 1024 * 100);
|
||||
printf("gaa\n");
|
||||
char *data = (char *)falloc(10);
|
||||
strcpy(data, "ab");
|
||||
char *data2 = (char *)falloc(30);
|
||||
strcpy(data2, "ff\n");
|
||||
|
||||
// for(int i = 0; i < 333333; i++)
|
||||
// strcat(data,"ggggggggggggggggg");
|
||||
|
||||
printf("%s\n", data);
|
||||
|
||||
// printf("%s\n",data);;
|
||||
|
||||
// strcpy(data2,"aaaa");
|
||||
|
||||
return 0;
|
||||
}
|
12
rhashtable.c
Normal file
12
rhashtable.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include "rhashtable.h"
|
||||
#include "rtest.h"
|
||||
#include "rstring.h"
|
||||
|
||||
int main() {
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
char *key = rgenerate_key();
|
||||
rset(key, "tast");
|
||||
rasserts(!strcmp(rget(key), "tast"));
|
||||
}
|
||||
}
|
67
rhashtable.h
Normal file
67
rhashtable.h
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef RHASHTABLE_H
|
||||
#define RHASHTABLE_H
|
||||
/*
|
||||
ORIGINAL SOURCE IS FROM K&R
|
||||
*/
|
||||
|
||||
#include "rmalloc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define HASHSIZE 101
|
||||
|
||||
// Structure for the table entries
|
||||
typedef struct rnlist {
|
||||
struct rnlist *next;
|
||||
char *name;
|
||||
char *defn;
|
||||
} rnlist;
|
||||
|
||||
// Hash table array
|
||||
static rnlist *rhashtab[HASHSIZE];
|
||||
|
||||
// Hash function
|
||||
unsigned rhash(char *s) {
|
||||
unsigned hashval;
|
||||
for (hashval = 0; *s != '\0'; s++)
|
||||
hashval = *s + 31 * hashval;
|
||||
return hashval % HASHSIZE;
|
||||
}
|
||||
|
||||
rnlist *rlget(char *s) {
|
||||
rnlist *np;
|
||||
for (np = rhashtab[rhash(s)]; np != NULL; np = np->next)
|
||||
if (strcmp(s, np->name) == 0)
|
||||
return np; // Found
|
||||
return NULL; // Not found
|
||||
}
|
||||
|
||||
// Lookup function
|
||||
char *rget(char *s) {
|
||||
rnlist *np = rlget(s);
|
||||
return np ? np->defn : NULL;
|
||||
}
|
||||
|
||||
// Install function (adds a name and definition to the table)
|
||||
struct rnlist *rset(char *name, char *defn) {
|
||||
struct rnlist *np = NULL;
|
||||
unsigned hashval;
|
||||
|
||||
if ((rlget(name)) == NULL) { // Not found
|
||||
np = (struct rnlist *)malloc(sizeof(*np));
|
||||
if (np == NULL || (np->name = strdup(name)) == NULL)
|
||||
return NULL;
|
||||
hashval = rhash(name);
|
||||
np->next = rhashtab[hashval];
|
||||
rhashtab[hashval] = np;
|
||||
} else {
|
||||
if (np->defn)
|
||||
free((void *)np->defn);
|
||||
np->defn = NULL;
|
||||
}
|
||||
if ((np->defn = strdup(defn)) == NULL)
|
||||
return NULL;
|
||||
return np;
|
||||
}
|
||||
#endif
|
72
rhttp.c
Normal file
72
rhttp.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include "rmalloc.h"
|
||||
#include "rhttp.h"
|
||||
#include "rtest.h"
|
||||
#include <pthread.h>
|
||||
|
||||
int request_handler(rhttp_request_t *r) {
|
||||
rhttp_send_drain(r->c,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: 3\r\n"
|
||||
"Connection: close\r\n\r\n"
|
||||
"Ok!",
|
||||
0);
|
||||
close(r->c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rhttp_request_handler_t handler = request_handler;
|
||||
|
||||
void *rhttp_serve_thread(void *port_arg) {
|
||||
int port = *(int *)port_arg;
|
||||
rhttp_serve(rhttp_opt_host, port, 1024, rhttp_opt_request_logging, rhttp_opt_debug, handler, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
bool do_test = true;
|
||||
int port = 9876;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--serve")) {
|
||||
printf("rhttp serve mode\n");
|
||||
printf("Handlers available:\n");
|
||||
printf(" - rhttp_root (/)\n");
|
||||
printf(" - rhttp_counter (/counter*)\n");
|
||||
printf(" - rhttp_404 (/*)\n");
|
||||
do_test = false;
|
||||
}
|
||||
if (!strcmp(argv[i], "--quiet")) {
|
||||
rhttp_opt_info = false;
|
||||
rhttp_opt_warn = false;
|
||||
rhttp_opt_request_logging = false;
|
||||
rhttp_opt_debug = false;
|
||||
printf("Quiet mode enabled\n");
|
||||
}
|
||||
if (atoi(argv[i])) {
|
||||
port = atoi(argv[i]);
|
||||
}
|
||||
}
|
||||
if (do_test) {
|
||||
rtest_banner("rhttp");
|
||||
} else {
|
||||
printf("Serving on %s:%d\n", rhttp_opt_host, port);
|
||||
handler = rhttp_default_request_handler;
|
||||
}
|
||||
|
||||
pthread_t st;
|
||||
pthread_create(&st, 0, rhttp_serve_thread, (void *)&port);
|
||||
|
||||
char *response = rhttp_client_get("127.0.0.1", port, "/");
|
||||
|
||||
if (do_test) {
|
||||
rassert(!strcmp(response, "Ok!"));
|
||||
pthread_cancel(st);
|
||||
// cleanup
|
||||
} else {
|
||||
pthread_join(st, NULL);
|
||||
}
|
||||
// rhttp_main(argc, argv);
|
||||
if (do_test)
|
||||
return rtest_end("");
|
||||
return 0;
|
||||
}
|
653
rhttp.h
Normal file
653
rhttp.h
Normal file
@ -0,0 +1,653 @@
|
||||
#ifndef RHTTP_H
|
||||
#define RHTTP_H
|
||||
#include "rio.h"
|
||||
#include "rmalloc.h"
|
||||
#include "rstring.h"
|
||||
#include "rtemp.h"
|
||||
#include "rtime.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BUFF_SIZE 8096
|
||||
#define RHTTP_MAX_CONNECTIONS 100
|
||||
|
||||
int rhttp_opt_error = 1;
|
||||
int rhttp_opt_warn = 1;
|
||||
int rhttp_opt_info = 1;
|
||||
int rhttp_opt_port = 8080;
|
||||
int rhttp_opt_debug = 0;
|
||||
int rhttp_opt_request_logging = 0;
|
||||
int rhttp_sock = 0;
|
||||
int rhttp_opt_buffered = 0;
|
||||
int rhttp_c = 0;
|
||||
int rhttp_c_mutex_initialized = 0;
|
||||
pthread_mutex_t rhttp_c_mutex;
|
||||
char rhttp_opt_host[1024] = "0.0.0.0";
|
||||
unsigned int rhttp_connections_handled = 0;
|
||||
|
||||
typedef struct rhttp_header_t {
|
||||
char *name;
|
||||
char *value;
|
||||
struct rhttp_header_t *next;
|
||||
} rhttp_header_t;
|
||||
|
||||
typedef struct rhttp_request_t {
|
||||
int c;
|
||||
int closed;
|
||||
bool keep_alive;
|
||||
nsecs_t start;
|
||||
char *raw;
|
||||
char *line;
|
||||
char *body;
|
||||
char *method;
|
||||
char *path;
|
||||
char *version;
|
||||
void *context;
|
||||
unsigned int bytes_received;
|
||||
rhttp_header_t *headers;
|
||||
} rhttp_request_t;
|
||||
|
||||
char *rhttp_current_timestamp() {
|
||||
time_t current_time;
|
||||
time(¤t_time);
|
||||
struct tm *local_time = localtime(¤t_time);
|
||||
static char time_string[100];
|
||||
time_string[0] = 0;
|
||||
strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", local_time);
|
||||
|
||||
return time_string;
|
||||
}
|
||||
|
||||
void rhttp_logs(const char *prefix, const char *level, const char *format, va_list args) {
|
||||
char buf[strlen(format) + BUFSIZ + 1];
|
||||
buf[0] = 0;
|
||||
sprintf(buf, "%s%s %s %s\e[0m", prefix, rhttp_current_timestamp(), level, format);
|
||||
vfprintf(stdout, buf, args);
|
||||
}
|
||||
void rhttp_log_info(const char *format, ...) {
|
||||
if (!rhttp_opt_info)
|
||||
return;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rhttp_logs("\e[32m", "INFO ", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rhttp_log_debug(const char *format, ...) {
|
||||
if (!rhttp_opt_debug)
|
||||
return;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (rhttp_opt_debug)
|
||||
rhttp_logs("\e[33m", "DEBUG", format, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
void rhttp_log_warn(const char *format, ...) {
|
||||
if (!rhttp_opt_warn)
|
||||
return;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rhttp_logs("\e[34m", "WARN ", format, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
void rhttp_log_error(const char *format, ...) {
|
||||
if (!rhttp_opt_error)
|
||||
return;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rhttp_logs("\e[35m", "ERROR", format, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void http_request_init(rhttp_request_t *r) {
|
||||
r->raw = NULL;
|
||||
r->line = NULL;
|
||||
r->body = NULL;
|
||||
r->method = NULL;
|
||||
r->path = NULL;
|
||||
r->version = NULL;
|
||||
r->start = 0;
|
||||
r->headers = NULL;
|
||||
r->bytes_received = 0;
|
||||
r->closed = 0;
|
||||
}
|
||||
|
||||
void rhttp_free_header(rhttp_header_t *h) {
|
||||
if (!h)
|
||||
return;
|
||||
rhttp_header_t *next = h->next;
|
||||
free(h->name);
|
||||
free(h->value);
|
||||
free(h);
|
||||
if (next)
|
||||
rhttp_free_header(next);
|
||||
}
|
||||
void rhttp_rhttp_free_headers(rhttp_request_t *r) {
|
||||
if (!r->headers)
|
||||
return;
|
||||
rhttp_free_header(r->headers);
|
||||
r->headers = NULL;
|
||||
}
|
||||
|
||||
rhttp_header_t *rhttp_parse_headers(rhttp_request_t *s) {
|
||||
int first = 1;
|
||||
char *body = strdup(s->body);
|
||||
char *body_original = body;
|
||||
while (body && *body) {
|
||||
char *line = __strtok_r(first ? body : NULL, "\r\n", &body);
|
||||
if (!line)
|
||||
break;
|
||||
rhttp_header_t *h = (rhttp_header_t *)malloc(sizeof(rhttp_header_t));
|
||||
h->name = NULL;
|
||||
h->value = NULL;
|
||||
h->next = NULL;
|
||||
char *name = __strtok_r(line, ": ", &line);
|
||||
first = 0;
|
||||
if (!name) {
|
||||
rhttp_free_header(h);
|
||||
break;
|
||||
}
|
||||
h->name = strdup(name);
|
||||
char *value = __strtok_r(NULL, "\r\n", &line);
|
||||
if (!value) {
|
||||
rhttp_free_header(h);
|
||||
break;
|
||||
}
|
||||
h->value = value ? strdup(value + 1) : strdup("");
|
||||
h->next = s->headers;
|
||||
s->headers = h;
|
||||
}
|
||||
free(body_original);
|
||||
return s->headers;
|
||||
}
|
||||
|
||||
void rhttp_free_request(rhttp_request_t *r) {
|
||||
if (r->raw) {
|
||||
free(r->raw);
|
||||
free(r->body);
|
||||
free(r->method);
|
||||
free(r->path);
|
||||
free(r->version);
|
||||
rhttp_rhttp_free_headers(r);
|
||||
}
|
||||
free(r);
|
||||
}
|
||||
|
||||
long rhttp_header_get_long(rhttp_request_t *r, const char *name) {
|
||||
rhttp_header_t *h = r->headers;
|
||||
while (h) {
|
||||
if (!strcmp(h->name, name))
|
||||
return strtol(h->value, NULL, 10);
|
||||
h = h->next;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
char *rhttp_header_get_string(rhttp_request_t *r, const char *name) {
|
||||
rhttp_header_t *h = r->headers;
|
||||
while (h) {
|
||||
if (!strcmp(h->name, name))
|
||||
return h->value && *h->value ? h->value : NULL;
|
||||
h = h->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rhttp_print_header(rhttp_header_t *h) { rhttp_log_debug("Header: <%s> \"%s\"\n", h->name, h->value); }
|
||||
void rhttp_print_headers(rhttp_header_t *h) {
|
||||
while (h) {
|
||||
rhttp_print_header(h);
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
void rhttp_print_request_line(rhttp_request_t *r) { rhttp_log_info("%s %s %s\n", r->method, r->path, r->version); }
|
||||
void rhttp_print_request(rhttp_request_t *r) {
|
||||
rhttp_print_request_line(r);
|
||||
if (rhttp_opt_debug)
|
||||
rhttp_print_headers(r->headers);
|
||||
}
|
||||
void rhttp_close(rhttp_request_t *r) {
|
||||
if (!r)
|
||||
return;
|
||||
if (!r->closed)
|
||||
close(r->c);
|
||||
rhttp_free_request(r);
|
||||
}
|
||||
rhttp_request_t *rhttp_parse_request(int s) {
|
||||
rhttp_request_t *request = (rhttp_request_t *)malloc(sizeof(rhttp_request_t));
|
||||
http_request_init(request);
|
||||
char buf[BUFF_SIZE] = {0};
|
||||
request->c = s;
|
||||
int breceived = 0;
|
||||
while (!rstrendswith(buf, "\r\n\r\n")) {
|
||||
int chunk_size = read(s, buf + breceived, 1);
|
||||
if (chunk_size <= 0) {
|
||||
close(request->c);
|
||||
request->closed = 1;
|
||||
return request;
|
||||
}
|
||||
breceived += chunk_size;
|
||||
}
|
||||
if (breceived <= 0) {
|
||||
close(request->c);
|
||||
request->closed = 1;
|
||||
return request;
|
||||
}
|
||||
buf[breceived] = '\0';
|
||||
char *original_buf = buf;
|
||||
|
||||
char *b = original_buf;
|
||||
request->raw = strdup(b);
|
||||
b = original_buf;
|
||||
char *line = strtok(b, "\r\n");
|
||||
b = original_buf;
|
||||
char *body = b + strlen(line) + 2;
|
||||
request->body = strdup(body);
|
||||
b = original_buf;
|
||||
char *method = strtok(b, " ");
|
||||
char *path = strtok(NULL, " ");
|
||||
char *version = strtok(NULL, " ");
|
||||
request->bytes_received = breceived;
|
||||
request->line = line;
|
||||
request->start = nsecs();
|
||||
request->method = strdup(method);
|
||||
request->path = strdup(path);
|
||||
request->version = strdup(version);
|
||||
request->headers = NULL;
|
||||
request->keep_alive = false;
|
||||
if (rhttp_parse_headers(request)) {
|
||||
char *keep_alive_string = rhttp_header_get_string(request, "Connection");
|
||||
if (keep_alive_string && !strcmp(keep_alive_string, "keep-alive")) {
|
||||
request->keep_alive = 1;
|
||||
}
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
void rhttp_close_server() {
|
||||
close(rhttp_sock);
|
||||
close(rhttp_c);
|
||||
printf("Connections handled: %d\n", rhttp_connections_handled);
|
||||
printf("Gracefully closed\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
size_t rhttp_send_drain(int s, void *tsend, size_t to_send_len) {
|
||||
if (to_send_len == 0 && *(unsigned char *)tsend) {
|
||||
to_send_len = strlen(tsend);
|
||||
}
|
||||
unsigned char *to_send = (unsigned char *)malloc(to_send_len);
|
||||
unsigned char *to_send_original = to_send;
|
||||
|
||||
memcpy(to_send, tsend, to_send_len);
|
||||
// to_send[to_send_len] = '\0';
|
||||
long bytes_sent = 0;
|
||||
long bytes_sent_total = 0;
|
||||
while (1) {
|
||||
bytes_sent = send(s, to_send + bytes_sent_total, to_send_len - bytes_sent_total, 0);
|
||||
if (bytes_sent <= 0) {
|
||||
bytes_sent_total = 0;
|
||||
break;
|
||||
}
|
||||
bytes_sent_total += bytes_sent;
|
||||
|
||||
if (bytes_sent_total == (long)to_send_len) {
|
||||
break;
|
||||
} else if (!bytes_sent) {
|
||||
bytes_sent_total = 0;
|
||||
// error
|
||||
break;
|
||||
} else {
|
||||
rhttp_log_info("Extra send of %d/%d bytes.\n", bytes_sent_total, to_send_len);
|
||||
}
|
||||
}
|
||||
|
||||
free(to_send_original);
|
||||
return bytes_sent_total;
|
||||
}
|
||||
|
||||
typedef int (*rhttp_request_handler_t)(rhttp_request_t *r);
|
||||
|
||||
void rhttp_serve(const char *host, int port, int backlog, int request_logging, int request_debug, rhttp_request_handler_t handler,
|
||||
void *context) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
rhttp_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = inet_addr(host ? host : "0.0.0.0");
|
||||
rhttp_opt_debug = request_debug;
|
||||
rhttp_opt_request_logging = request_logging;
|
||||
int opt = 1;
|
||||
setsockopt(rhttp_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
if (bind(rhttp_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
printf("Binding error\n");
|
||||
exit(1);
|
||||
}
|
||||
listen(rhttp_sock, backlog);
|
||||
while (1) {
|
||||
struct sockaddr_in client_addr;
|
||||
int addrlen = sizeof(client_addr);
|
||||
|
||||
rhttp_c = accept(rhttp_sock, (struct sockaddr *)&client_addr, (socklen_t *)&addrlen);
|
||||
|
||||
rhttp_connections_handled++;
|
||||
while (true) {
|
||||
rhttp_request_t *r = rhttp_parse_request(rhttp_c);
|
||||
r->context = context;
|
||||
if (!r->closed) {
|
||||
if (!handler(r) && !r->closed) {
|
||||
rhttp_close(r);
|
||||
}
|
||||
}
|
||||
if (!r->keep_alive && !r->closed) {
|
||||
rhttp_close(r);
|
||||
} else if (r->keep_alive && !r->closed) {
|
||||
}
|
||||
if (r->closed) {
|
||||
break;
|
||||
}
|
||||
rhttp_free_request(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int rhttp_calculate_number_char_count(unsigned int number) {
|
||||
unsigned int width = 1;
|
||||
unsigned int tcounter = number;
|
||||
while (tcounter / 10 >= 1) {
|
||||
tcounter = tcounter / 10;
|
||||
width++;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
int rhttp_file_response(rhttp_request_t *r, char *path) {
|
||||
if (!*path)
|
||||
return 0;
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (f == NULL)
|
||||
return 0;
|
||||
size_t file_size = rfile_size(path);
|
||||
char response[1024] = {0};
|
||||
char content_type_header[100] = {0};
|
||||
char *ext = strstr(path, ".");
|
||||
char *text_extensions = ".h,.c,.html";
|
||||
if (strstr(text_extensions, ext)) {
|
||||
sprintf(content_type_header, "Content-Type: %s\r\n", "text/html");
|
||||
}
|
||||
sprintf(response, "HTTP/1.1 200 OK\r\n%sContent-Length:%ld\r\n\r\n", content_type_header, file_size);
|
||||
if (!rhttp_send_drain(r->c, response, 0)) {
|
||||
rhttp_log_error("Error sending file: %s\n", path);
|
||||
}
|
||||
size_t bytes = 0;
|
||||
size_t bytes_sent = 0;
|
||||
unsigned char file_buff[1024];
|
||||
while ((bytes = fread(file_buff, sizeof(char), sizeof(file_buff), f))) {
|
||||
if (!rhttp_send_drain(r->c, file_buff, bytes)) {
|
||||
rhttp_log_error("Error sending file during chunking: %s\n", path);
|
||||
}
|
||||
bytes_sent += bytes;
|
||||
}
|
||||
if (bytes_sent != file_size) {
|
||||
rhttp_send_drain(r->c, file_buff, file_size - bytes_sent);
|
||||
}
|
||||
close(r->c);
|
||||
fclose(f);
|
||||
return 1;
|
||||
};
|
||||
|
||||
int rhttp_file_request_handler(rhttp_request_t *r) {
|
||||
char *path = r->path;
|
||||
while (*path == '/' || *path == '.')
|
||||
path++;
|
||||
if (strstr(path, "..")) {
|
||||
return 0;
|
||||
}
|
||||
return rhttp_file_response(r, path);
|
||||
};
|
||||
|
||||
unsigned int counter = 100000000;
|
||||
int rhttp_counter_request_handler(rhttp_request_t *r) {
|
||||
if (!strncmp(r->path, "/counter", strlen("/counter"))) {
|
||||
counter++;
|
||||
unsigned int width = rhttp_calculate_number_char_count(counter);
|
||||
char to_send2[1024] = {0};
|
||||
sprintf(to_send2,
|
||||
"HTTP/1.1 200 OK\r\nContent-Length: %d\r\nConnection: "
|
||||
"close\r\n\r\n%d",
|
||||
width, counter);
|
||||
rhttp_send_drain(r->c, to_send2, 0);
|
||||
close(r->c);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int rhttp_root_request_handler(rhttp_request_t *r) {
|
||||
if (!strcmp(r->path, "/")) {
|
||||
char to_send[1024] = {0};
|
||||
sprintf(to_send, "HTTP/1.1 200 OK\r\nContent-Length: 3\r\nConnection: "
|
||||
"close\r\n\r\nOk!");
|
||||
rhttp_send_drain(r->c, to_send, 0);
|
||||
close(r->c);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int rhttp_error_404_handler(rhttp_request_t *r) {
|
||||
char to_send[1024] = {0};
|
||||
sprintf(to_send, "HTTP/1.1 404 Document not found\r\nContent-Length: "
|
||||
"0\r\nConnection: close\r\n\r\n");
|
||||
rhttp_send_drain(r->c, to_send, 0);
|
||||
close(r->c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rhttp_default_request_handler(rhttp_request_t *r) {
|
||||
if (rhttp_opt_debug || rhttp_opt_request_logging)
|
||||
rhttp_print_request(r);
|
||||
if (rhttp_counter_request_handler(r)) {
|
||||
// Counter handler
|
||||
rhttp_log_info("Counter handler found for: %s\n", r->path);
|
||||
|
||||
} else if (rhttp_root_request_handler(r)) {
|
||||
// Root handler
|
||||
rhttp_log_info("Root handler found for: %s\n", r->path);
|
||||
} else if (rhttp_file_request_handler(r)) {
|
||||
rhttp_log_info("File %s sent\n", r->path);
|
||||
} else if (rhttp_error_404_handler(r)) {
|
||||
rhttp_log_warn("Error 404 for: %s\n", r->path);
|
||||
// Error handler
|
||||
} else {
|
||||
rhttp_log_warn("No handler found for: %s\n", r->path);
|
||||
close(rhttp_c);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rhttp_main(int argc, char *argv[]) {
|
||||
setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "p:drh:bewi")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
rhttp_opt_info = 1;
|
||||
rhttp_opt_warn = 1;
|
||||
rhttp_opt_error = 1;
|
||||
break;
|
||||
case 'e':
|
||||
rhttp_opt_error = 1;
|
||||
rhttp_opt_warn = 0;
|
||||
rhttp_opt_info = 0;
|
||||
break;
|
||||
case 'w':
|
||||
rhttp_opt_warn = 1;
|
||||
rhttp_opt_error = 1;
|
||||
rhttp_opt_info = 0;
|
||||
break;
|
||||
case 'p':
|
||||
rhttp_opt_port = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
rhttp_opt_buffered = 1;
|
||||
printf("Logging is buffered. Output may be incomplete.\n");
|
||||
break;
|
||||
case 'h':
|
||||
strcpy(rhttp_opt_host, optarg);
|
||||
break;
|
||||
case 'd':
|
||||
printf("Debug enabled\n");
|
||||
rhttp_opt_debug = 1;
|
||||
rhttp_opt_warn = 1;
|
||||
rhttp_opt_info = 1;
|
||||
rhttp_opt_error = 1;
|
||||
break;
|
||||
case 'r':
|
||||
printf("Request logging enabled\n");
|
||||
rhttp_opt_request_logging = 1;
|
||||
break;
|
||||
default:
|
||||
printf("Usage: %s [-p port] [-h host] [-b]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Starting server on: %s:%d\n", rhttp_opt_host, rhttp_opt_port);
|
||||
if (rhttp_opt_buffered)
|
||||
setvbuf(stdout, NULL, _IOFBF, BUFSIZ);
|
||||
|
||||
rhttp_serve(rhttp_opt_host, rhttp_opt_port, 1024, rhttp_opt_request_logging, rhttp_opt_debug, rhttp_default_request_handler, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CLIENT CODE */
|
||||
|
||||
typedef struct rhttp_client_request_t {
|
||||
char *host;
|
||||
int port;
|
||||
char *path;
|
||||
bool is_done;
|
||||
char *request;
|
||||
char *response;
|
||||
pthread_t thread;
|
||||
int bytes_received;
|
||||
} rhttp_client_request_t;
|
||||
|
||||
rhttp_client_request_t *rhttp_create_request(const char *host, int port, const char *path) {
|
||||
rhttp_client_request_t *r = (rhttp_client_request_t *)malloc(sizeof(rhttp_client_request_t));
|
||||
char request_line[4096] = {0};
|
||||
sprintf(request_line,
|
||||
"GET %s HTTP/1.1\r\n"
|
||||
"Host: localhost:8000\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Accept: */*\r\n"
|
||||
"User-Agent: mhttpc\r\n"
|
||||
"Accept-Language: en-US,en;q=0.5\r\n"
|
||||
"Accept-Encoding: gzip, deflate\r\n"
|
||||
"\r\n",
|
||||
path);
|
||||
r->request = strdup(request_line);
|
||||
r->host = strdup(host);
|
||||
r->port = port;
|
||||
r->path = strdup(path);
|
||||
r->is_done = false;
|
||||
r->response = NULL;
|
||||
r->bytes_received = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
int rhttp_execute_request(rhttp_client_request_t *r) {
|
||||
int s = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in addr;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(r->port);
|
||||
addr.sin_addr.s_addr = inet_addr(r->host);
|
||||
|
||||
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
send(s, r->request, strlen(r->request), 0);
|
||||
char buf[1024 * 1024] = {0};
|
||||
int ret = recv(s, buf, 1024 * 1024, 0);
|
||||
if (ret > 0) {
|
||||
r->response = strdup(buf);
|
||||
}
|
||||
|
||||
close(s);
|
||||
return ret;
|
||||
}
|
||||
void rhttp_reset_request(rhttp_client_request_t *r) {
|
||||
free(r->response);
|
||||
r->is_done = false;
|
||||
r->response = NULL;
|
||||
r->bytes_received = 0;
|
||||
}
|
||||
void rhttp_free_client_request(rhttp_client_request_t *r) {
|
||||
if (r->request)
|
||||
free(r->request);
|
||||
if (r->response)
|
||||
free(r->response);
|
||||
if (r->host)
|
||||
free(r->host);
|
||||
if (r->path)
|
||||
free(r->path);
|
||||
free(r);
|
||||
}
|
||||
|
||||
void rhttp_client_bench(int workers, int times, const char *host, int port, const char *path) {
|
||||
rhttp_client_request_t *requests[workers];
|
||||
while (times > 0) {
|
||||
|
||||
for (int i = 0; i < workers && times; i++) {
|
||||
requests[i] = rhttp_create_request(host, port, path);
|
||||
rhttp_execute_request(requests[i]);
|
||||
times--;
|
||||
}
|
||||
}
|
||||
}
|
||||
char *rhttp_client_get(const char *host, int port, const char *path) {
|
||||
if (!rhttp_c_mutex_initialized) {
|
||||
rhttp_c_mutex_initialized = 1;
|
||||
pthread_mutex_init(&rhttp_c_mutex, NULL);
|
||||
}
|
||||
char http_response[1024 * 1024];
|
||||
http_response[0] = 0;
|
||||
rhttp_client_request_t *r = rhttp_create_request(host, port, path);
|
||||
unsigned int reconnects = 0;
|
||||
unsigned int reconnects_max = 100000;
|
||||
while (!rhttp_execute_request(r)) {
|
||||
reconnects++;
|
||||
tick();
|
||||
if (reconnects == reconnects_max) {
|
||||
fprintf(stderr, "Maxium reconnects exceeded for %s:%d\n", host, port);
|
||||
rhttp_free_client_request(r);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
r->is_done = true;
|
||||
char *body = r->response ? strstr(r->response, "\r\n\r\n") : NULL;
|
||||
pthread_mutex_lock(&rhttp_c_mutex);
|
||||
if (body) {
|
||||
strcpy(http_response, body + 4);
|
||||
} else {
|
||||
strcpy(http_response, r->response);
|
||||
}
|
||||
rhttp_free_client_request(r);
|
||||
char *result = sbuf(http_response);
|
||||
pthread_mutex_unlock(&rhttp_c_mutex);
|
||||
return result;
|
||||
}
|
||||
/*END CLIENT CODE */
|
||||
#endif
|
11
rhttpc.c
Normal file
11
rhttpc.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include "rhttp.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
while (1) {
|
||||
char *response = rhttp_client_get("127.0.0.1", 8888, "/");
|
||||
if (response)
|
||||
printf("%s\n", response);
|
||||
}
|
||||
return 0;
|
||||
}
|
336
ricli.c
Normal file
336
ricli.c
Normal file
@ -0,0 +1,336 @@
|
||||
#include "rterm.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "rstring.h"
|
||||
#include "rrex4.h"
|
||||
#include <limits.h>
|
||||
#include "rautocomplete.h"
|
||||
|
||||
typedef struct ricli_line_t {
|
||||
unsigned int index;
|
||||
char type[20];
|
||||
size_t length;
|
||||
char *content;
|
||||
} ricli_line_t;
|
||||
|
||||
ricli_line_t *ricli_line_new() {
|
||||
ricli_line_t *line = (ricli_line_t *)malloc(sizeof(ricli_line_t));
|
||||
line->index = 0;
|
||||
memset(line->type, 0, sizeof(line->type));
|
||||
line->length = 0;
|
||||
line->content = NULL;
|
||||
return line;
|
||||
}
|
||||
|
||||
char *rscli_line_to_json(ricli_line_t *line) {
|
||||
|
||||
char *json = (char *)malloc(sizeof(line->type) + strlen(line->content) * 2 + 10);
|
||||
json[0] = 0;
|
||||
strcpy(json, "{\"type\":\"");
|
||||
strcat(json, line->type);
|
||||
strcat(json, "\",\"content\":\"");
|
||||
char content_safe[strlen(line->content) * 2];
|
||||
content_safe[0] = 0;
|
||||
rstraddslashes(line->content, content_safe);
|
||||
strcat(json, content_safe);
|
||||
strcat(json, "\"}");
|
||||
return json;
|
||||
}
|
||||
typedef struct ricli_t {
|
||||
ricli_line_t **lines;
|
||||
int line_count;
|
||||
bool line_numbers;
|
||||
char input[1024 * 5];
|
||||
unsigned int history_index;
|
||||
unsigned int x;
|
||||
bool auto_save;
|
||||
rautocomplete_t *autocomplete;
|
||||
char history_file[FILENAME_MAX];
|
||||
bool reset;
|
||||
void (*before_add_line)(struct ricli_t *r);
|
||||
void (*after_add_line)(struct ricli_t *r);
|
||||
void (*keypress)(struct ricli_t *);
|
||||
void (*before_draw)(struct ricli_t *);
|
||||
rterm_t *term;
|
||||
} ricli_t;
|
||||
|
||||
void ricli_keypress(rterm_t *rt);
|
||||
void ricli_before_draw(rterm_t *rt);
|
||||
void ricli_save(ricli_t *cli, char *path);
|
||||
void ricli_autocomplete_execute(ricli_t *cli);
|
||||
void ricli_add_autocomplete(ricli_t *cli, char *str) {
|
||||
if (rautocomplete_contains(cli->autocomplete, str))
|
||||
return;
|
||||
rautocomplete_add(cli->autocomplete, str);
|
||||
}
|
||||
|
||||
ricli_line_t *ricli_get_last_line(ricli_t *r) {
|
||||
if (!r->line_count) {
|
||||
return NULL;
|
||||
}
|
||||
return r->lines[r->line_count - 1];
|
||||
}
|
||||
|
||||
void ricli_after_draw(rterm_t *rt) {
|
||||
ricli_t *r = (ricli_t *)rt->session;
|
||||
ricli_autocomplete_execute(r);
|
||||
}
|
||||
|
||||
ricli_t *ricli_terminal_new() {
|
||||
ricli_t *terminal = (ricli_t *)malloc(sizeof(ricli_t));
|
||||
terminal->lines = NULL;
|
||||
terminal->line_count = 0;
|
||||
terminal->line_numbers = false;
|
||||
terminal->reset = true;
|
||||
terminal->history_index = 0;
|
||||
terminal->before_add_line = NULL;
|
||||
terminal->term = NULL;
|
||||
terminal->history_file[0] = 0;
|
||||
terminal->autocomplete = rautocomplete_new();
|
||||
terminal->auto_save = true;
|
||||
terminal->x = 0;
|
||||
memset(terminal->input, 0, sizeof(terminal->input));
|
||||
terminal->term = (rterm_t *)malloc(sizeof(rterm_t));
|
||||
|
||||
rterm_init(terminal->term);
|
||||
terminal->line_numbers = true;
|
||||
|
||||
terminal->term->after_key_press = ricli_keypress;
|
||||
terminal->term->before_draw = ricli_before_draw;
|
||||
terminal->term->after_draw = ricli_after_draw;
|
||||
terminal->term->session = (void *)terminal;
|
||||
return terminal;
|
||||
}
|
||||
void ricli_set_input(ricli_t *cli, const char *content);
|
||||
void ricli_autocomplete_execute(ricli_t *r) {
|
||||
char *result = rautocomplete_find(r->autocomplete, r->input);
|
||||
unsigned int original_x = r->term->cursor.x;
|
||||
unsigned int original_y = r->term->cursor.y;
|
||||
if (result && result[0] != 1) {
|
||||
original_x = r->x;
|
||||
cursor_set(r->term, 0, r->term->size.ws_row - 1);
|
||||
printf("(%d)%s", result[0], result);
|
||||
cursor_set(r->term, original_x, original_y);
|
||||
}
|
||||
}
|
||||
void ricli_add_line(ricli_t *r, char *type, char *content) {
|
||||
|
||||
ricli_line_t *line = ricli_line_new();
|
||||
strcpy(line->type, type ? type : "");
|
||||
line->content = (char *)malloc(strlen(content ? content : "") + 1);
|
||||
strcpy(line->content, content ? content : "");
|
||||
line->length = strlen(line->content);
|
||||
if (line->length && line->content[line->length - 1] == '\n') {
|
||||
line->content[line->length - 1] = 0;
|
||||
line->length--;
|
||||
}
|
||||
if (line->length)
|
||||
ricli_add_autocomplete(r, line->content);
|
||||
strcpy(line->type, type ? type : "");
|
||||
line->index = r->line_count;
|
||||
r->lines = realloc(r->lines, sizeof(ricli_line_t *) * (r->line_count + 1));
|
||||
r->lines[r->line_count] = line;
|
||||
r->line_count++;
|
||||
r->history_index = r->line_count;
|
||||
r->x = 0;
|
||||
|
||||
if (r->history_file[0] && r->auto_save)
|
||||
ricli_save(r, r->history_file);
|
||||
}
|
||||
|
||||
ricli_t *rt_get_ricli(rterm_t *rt) { return (ricli_t *)rt->session; }
|
||||
|
||||
void ricli_reset(rterm_t *rt) {
|
||||
ricli_t *cli = rt_get_ricli(rt);
|
||||
cli->reset = false;
|
||||
cursor_set(rt, 0, rt->size.ws_row - 1);
|
||||
}
|
||||
|
||||
void ricli_before_draw(rterm_t *rt) {
|
||||
ricli_t *cli = rt_get_ricli(rt);
|
||||
int offset = 0;
|
||||
if (cli->line_count > rt->size.ws_row - 1) {
|
||||
offset = cli->line_count - rt->size.ws_row;
|
||||
}
|
||||
for (int i = offset; i < cli->line_count; i++) {
|
||||
printf("%.5d %s\n", i + 1, cli->lines[i]->content);
|
||||
}
|
||||
rt->status_text = cli->input;
|
||||
if (cli->reset) {
|
||||
ricli_reset(rt);
|
||||
}
|
||||
}
|
||||
|
||||
void ricli_clear_input(ricli_t *cli) {
|
||||
char line[cli->term->size.ws_col + 1];
|
||||
memset(line, ' ', sizeof(line));
|
||||
line[sizeof(line) - 1] = 0;
|
||||
cursor_set(cli->term, 0, cli->term->cursor.y);
|
||||
}
|
||||
void ricli_set_input(ricli_t *cli, const char *content) {
|
||||
if (cli->input != content) {
|
||||
memset(cli->input, 0, sizeof(cli->input));
|
||||
strcpy(cli->input, content);
|
||||
}
|
||||
strcpy(cli->term->status_text, cli->input);
|
||||
ricli_clear_input(cli);
|
||||
rterm_print_status_bar(cli->term, 'c', cli->input);
|
||||
cursor_set(cli->term, cli->x, cli->term->size.ws_row);
|
||||
}
|
||||
void ricli_put_input(ricli_t *cli, char c) {
|
||||
bool was_zero = cli->input[cli->x] == 0;
|
||||
if (was_zero) {
|
||||
|
||||
cli->input[cli->x] = c;
|
||||
cli->input[cli->x + 1] = 0;
|
||||
} else {
|
||||
char line_first[strlen(cli->input) + 5];
|
||||
|
||||
memset(line_first, 0, sizeof(line_first));
|
||||
line_first[0] = 0;
|
||||
strncpy(line_first, cli->input, cli->x);
|
||||
|
||||
char line_end[strlen(cli->input) + 2];
|
||||
memset(line_end, 0, sizeof(line_end));
|
||||
|
||||
char *input_ptr = cli->input;
|
||||
strcpy(line_end, input_ptr + cli->x);
|
||||
char new_char[] = {c, 0};
|
||||
strcat(line_first, new_char);
|
||||
strcat(line_first, line_end);
|
||||
memset(cli->input, 0, sizeof(cli->input));
|
||||
strcpy(cli->input, line_first);
|
||||
}
|
||||
cli->history_index = cli->line_count;
|
||||
rterm_print_status_bar(cli->term, 'c', cli->input);
|
||||
|
||||
if (cli->x >= strlen(cli->input))
|
||||
cli->x = strlen(cli->input) - 1;
|
||||
cli->x++;
|
||||
cursor_set(cli->term, cli->x, cli->term->cursor.y);
|
||||
}
|
||||
|
||||
void ricli_load(ricli_t *cli, char *path) {
|
||||
strcpy(cli->history_file, path);
|
||||
size_t size = rfile_size(path);
|
||||
if (size == 0) {
|
||||
|
||||
return;
|
||||
}
|
||||
char *data = malloc(size + 1);
|
||||
memset(data, 0, size + 1);
|
||||
rfile_readb(path, data, size);
|
||||
r4_t *r = r4(data, "\"type\":\"(.*)\",\"content\":\"(.*)\"");
|
||||
while (r->match_count == 2) {
|
||||
char stripped_slashes[strlen(r->matches[1]) + 1];
|
||||
memset(stripped_slashes, 0, sizeof(stripped_slashes));
|
||||
rstrstripslashes(r->matches[1], stripped_slashes);
|
||||
ricli_add_line(cli, r->matches[0], stripped_slashes);
|
||||
r4_next(r, NULL);
|
||||
}
|
||||
r4_free(r);
|
||||
free(data);
|
||||
}
|
||||
void ricli_save(ricli_t *cli, char *path) {
|
||||
FILE *f = fopen(path, "w+");
|
||||
for (int i = 0; i < cli->line_count; i++) {
|
||||
if (!cli->lines[i]->length)
|
||||
continue;
|
||||
char *json_line = rscli_line_to_json(cli->lines[i]);
|
||||
if (i != cli->line_count - 1) {
|
||||
strcat(json_line, ",");
|
||||
}
|
||||
fwrite(json_line, 1, strlen(json_line), f);
|
||||
free(json_line);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
ricli_delete_input(ricli_t *cli, unsigned int index) {
|
||||
if (cli->input[index + 1] == 0) {
|
||||
cli->input[index] = 0;
|
||||
} else {
|
||||
char new_line[strlen(cli->input) + 5];
|
||||
memset(new_line, 0, sizeof(new_line));
|
||||
strncpy(new_line, cli->input, index);
|
||||
char *input_ptr = cli->input;
|
||||
strcat(new_line, input_ptr + index + 1);
|
||||
strcpy(cli->input, new_line);
|
||||
}
|
||||
cursor_set(cli->term, cli->x, cli->term->cursor.y);
|
||||
}
|
||||
|
||||
void ricli_keypress(rterm_t *rt) {
|
||||
ricli_t *cli = rt_get_ricli(rt);
|
||||
if (rt->key.c == 10) {
|
||||
if (cli->input[rt->cursor.x] == 0) {
|
||||
cli->input[rt->cursor.x] = '\n';
|
||||
cli->input[rt->cursor.x + 1] = 0;
|
||||
}
|
||||
if (cli->before_add_line) {
|
||||
cli->before_add_line(cli);
|
||||
}
|
||||
ricli_add_line(cli, "user", cli->input);
|
||||
cursor_set(rt, 0, rt->cursor.y);
|
||||
memset(cli->input, 0, sizeof(cli->input));
|
||||
cli->history_index = cli->line_count;
|
||||
if (cli->after_add_line) {
|
||||
cli->after_add_line(cli);
|
||||
}
|
||||
} else if (rt->key.escape && rt->key.c == 'A') {
|
||||
if (cli->history_index != 0)
|
||||
cli->history_index--;
|
||||
|
||||
strcpy(cli->input, cli->lines[cli->history_index]->content);
|
||||
cli->x = strlen(cli->input);
|
||||
ricli_set_input(cli, cli->lines[cli->history_index]->content);
|
||||
|
||||
} else if (rt->key.c == 127) {
|
||||
|
||||
if (cli->x > 0) {
|
||||
cli->x--;
|
||||
cli->input[cli->x] = 0;
|
||||
ricli_delete_input(cli, cli->x);
|
||||
}
|
||||
} else if (rt->key.escape && rt->key.c == 'B') {
|
||||
if (cli->history_index < cli->line_count - 1) {
|
||||
cli->history_index++;
|
||||
|
||||
strcpy(cli->input, cli->lines[cli->history_index]->content);
|
||||
cli->x = strlen(cli->input);
|
||||
ricli_set_input(cli, cli->lines[cli->history_index]->content);
|
||||
|
||||
} else {
|
||||
cli->x = 0;
|
||||
ricli_set_input(cli, "");
|
||||
cli->history_index = cli->line_count;
|
||||
}
|
||||
|
||||
} else if (rt->key.escape && rt->key.c == 'D') {
|
||||
cli->x = rt->cursor.x;
|
||||
cursor_set(rt, cli->x, rt->cursor.y);
|
||||
} else if (rt->key.escape && rt->key.c == 'C') {
|
||||
cli->x = rt->cursor.x;
|
||||
if (cli->x > strlen(cli->input))
|
||||
cli->x = strlen(cli->input);
|
||||
cursor_set(rt, cli->x, rt->cursor.y);
|
||||
} else if (!rt->key.escape) {
|
||||
if (rt->cursor.x > strlen(cli->input)) {
|
||||
rt->cursor.x = strlen(cli->input);
|
||||
cli->x = strlen(cli->input);
|
||||
}
|
||||
ricli_put_input(cli, rt->key.c);
|
||||
ricli_autocomplete_execute(cli);
|
||||
}
|
||||
// rterm_print_status_bar(rt, 0, 0);
|
||||
}
|
||||
|
||||
void ricli_loop(ricli_t *r) { rterm_loop(r->term); }
|
||||
|
||||
int main() {
|
||||
|
||||
ricli_t *cli = ricli_terminal_new();
|
||||
ricli_load(cli, "/tmp/.ricli.json");
|
||||
ricli_loop(cli);
|
||||
return 0;
|
||||
}
|
40
rinterp.c
Normal file
40
rinterp.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include "rlexer.c";
|
||||
|
||||
typedef enum rip_ast_type_t { RIP_NONE = 0, RIP_BLOCK, RIP_CALL, RIP_LITERAL } rip_ast_type_t;
|
||||
|
||||
typedef struct rip_ast_t {
|
||||
struct rip_ast_t *children;
|
||||
struct rip_ast_t *next;
|
||||
struct rip_ast_t *previous;
|
||||
rip_ast_type_t type;
|
||||
} rip_ast_t;
|
||||
|
||||
rip_ast_t *rip_ast_new() {
|
||||
rip_ast_t *ast = (rip_ast_t *)malloc(sizeof(rip_ast_t));
|
||||
ast->children = NULL;
|
||||
ast->next = NULL;
|
||||
ast->previous = NULL;
|
||||
ast->type = RIP_NONE;
|
||||
return ast;
|
||||
}
|
||||
|
||||
rip_ast_t *rip_parse() {
|
||||
rtoken_t token = rlex_next();
|
||||
if (token.type == RT_CURLY_BRACE_OPEN) {
|
||||
rip_ast_t *ast = rip_ast_new();
|
||||
while ()
|
||||
rip_ast_t *statement = rip_parse();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
char *script = "{print(\"test\")}";
|
||||
rlex(script);
|
||||
while (true) {
|
||||
rtoken_t token = rlex_next();
|
||||
if (token.type = RT_CURLY_BRACE_OPEN) {
|
||||
rclos
|
||||
}
|
||||
}
|
||||
}
|
9
rio.c
Normal file
9
rio.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include "rio.h"
|
||||
|
||||
void cb(char *str) { printf("%s\n", str); }
|
||||
|
||||
int main() {
|
||||
rforfile("/tmp", cb);
|
||||
|
||||
return 0;
|
||||
}
|
117
rio.h
Normal file
117
rio.h
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef RLIB_RIO
|
||||
#define RLIB_RIO
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/select.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/dir.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include "rstring_list.h"
|
||||
|
||||
bool rfile_exists(char *path) {
|
||||
struct stat s;
|
||||
return !stat(path, &s);
|
||||
}
|
||||
|
||||
void rjoin_path(char *p1, char *p2, char *output) {
|
||||
output[0] = 0;
|
||||
strcpy(output, p1);
|
||||
|
||||
if (output[strlen(output) - 1] != '/') {
|
||||
char slash[] = "/";
|
||||
strcat(output, slash);
|
||||
}
|
||||
if (p2[0] == '/') {
|
||||
p2++;
|
||||
}
|
||||
strcat(output, p2);
|
||||
}
|
||||
|
||||
int risprivatedir(const char *path) {
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat(path, &statbuf) != 0) {
|
||||
perror("stat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(statbuf.st_mode)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ((statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == S_IRWXU) {
|
||||
return 1; // Private (owner has all permissions, others have none)
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
bool risdir(const char *path) { return !risprivatedir(path); }
|
||||
|
||||
void rforfile(char *path, void callback(char *)) {
|
||||
if (!rfile_exists(path))
|
||||
return;
|
||||
DIR *dir = opendir(path);
|
||||
struct dirent *d;
|
||||
while ((d = readdir(dir)) != NULL) {
|
||||
if (!d)
|
||||
break;
|
||||
|
||||
if ((d->d_name[0] == '.' && strlen(d->d_name) == 1) || d->d_name[1] == '.') {
|
||||
continue;
|
||||
}
|
||||
char full_path[4096];
|
||||
rjoin_path(path, d->d_name, full_path);
|
||||
|
||||
if (risdir(full_path)) {
|
||||
callback(full_path);
|
||||
rforfile(full_path, callback);
|
||||
} else {
|
||||
callback(full_path);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
bool rfd_wait(int fd, int ms) {
|
||||
|
||||
fd_set read_fds;
|
||||
struct timeval timeout;
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(fd, &read_fds);
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 1000 * ms;
|
||||
|
||||
int ret = select(fd + 1, &read_fds, NULL, NULL, &timeout);
|
||||
return ret > 0 && FD_ISSET(fd, &read_fds);
|
||||
}
|
||||
|
||||
bool rfd_wait_forever(int fd) {
|
||||
while ((!rfd_wait(fd, 10))) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t rfile_size(char *path) {
|
||||
struct stat s;
|
||||
stat(path, &s);
|
||||
return s.st_size;
|
||||
}
|
||||
|
||||
size_t rfile_readb(char *path, void *data, size_t size) {
|
||||
FILE *fd = fopen(path, "r");
|
||||
if (!fd) {
|
||||
return 0;
|
||||
}
|
||||
size_t bytes_read = fread(data, sizeof(char), size, fd);
|
||||
|
||||
fclose(fd);
|
||||
((char *)data)[bytes_read] = 0;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
#endif
|
19
rjson.c
Normal file
19
rjson.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include "rjson.h"
|
||||
|
||||
int main() {
|
||||
rtest_banner("rjson");
|
||||
rjson_t *json = rjson();
|
||||
rjson_array_start(json);
|
||||
rjson_object_start(json);
|
||||
rjson_kv_string(json, "string", "value");
|
||||
rjson_kv_number(json, "number", 1337421984);
|
||||
rjson_kv_duration(json, "duration", 1337421984);
|
||||
rjson_kv_int(json, "ulonglong", 1337420);
|
||||
rjson_kv_bool(json, "bool", true);
|
||||
rjson_object_close(json);
|
||||
rjson_array_close(json);
|
||||
rassert(!strcmp(json->content, "[{\"string\":\"value\",\"number\":\"1.337.421.984\",\"duration\":\"1."
|
||||
"34s\",\"ulonglong\":1337420,\"bool\":true}]"));
|
||||
rjson_free(json);
|
||||
return rtest_end("");
|
||||
}
|
130
rjson.h
Normal file
130
rjson.h
Normal file
@ -0,0 +1,130 @@
|
||||
#ifndef RJSON_H
|
||||
#define RJSON_H
|
||||
#include "rmalloc.h"
|
||||
#include "rtypes.h"
|
||||
#include "rstring.h"
|
||||
#include "rtemp.h"
|
||||
#include "rtime.h"
|
||||
#include "rtest.h"
|
||||
|
||||
typedef struct rjson_t {
|
||||
char *content;
|
||||
size_t length;
|
||||
size_t size;
|
||||
} rjson_t;
|
||||
|
||||
rjson_t *rjson() {
|
||||
rjson_t *json = rmalloc(sizeof(rjson_t));
|
||||
json->size = 1024;
|
||||
json->length = 0;
|
||||
json->content = (char *)rmalloc(json->size);
|
||||
json->content[0] = 0;
|
||||
return json;
|
||||
}
|
||||
|
||||
void rjson_write(rjson_t *rjs, char *content) {
|
||||
size_t len = strlen(content);
|
||||
while (rjs->size < rjs->length + len + 1) {
|
||||
rjs->content = realloc(rjs->content, rjs->size + 1024);
|
||||
rjs->size += 1024;
|
||||
}
|
||||
strcat(rjs->content, content);
|
||||
rjs->length += len;
|
||||
}
|
||||
|
||||
void rjson_object_start(rjson_t *rjs) {
|
||||
if (rstrendswith(rjs->content, "}"))
|
||||
rjson_write(rjs, ",");
|
||||
rjson_write(rjs, "{");
|
||||
}
|
||||
void rjson_object_close(rjson_t *rjs) {
|
||||
if (rstrendswith(rjs->content, ",")) {
|
||||
rjs->content[rjs->length - 1] = 0;
|
||||
rjs->length--;
|
||||
}
|
||||
rjson_write(rjs, "}");
|
||||
}
|
||||
void rjson_array_start(rjson_t *rjs) {
|
||||
if (rjs->length && (rstrendswith(rjs->content, "}") || rstrendswith(rjs->content, "]")))
|
||||
rjson_write(rjs, ",");
|
||||
rjson_write(rjs, "[");
|
||||
}
|
||||
void rjson_array_close(rjson_t *rjs) {
|
||||
if (rstrendswith(rjs->content, ",")) {
|
||||
rjs->content[rjs->length - 1] = 0;
|
||||
rjs->length--;
|
||||
}
|
||||
rjson_write(rjs, "]");
|
||||
}
|
||||
|
||||
void rjson_kv_string(rjson_t *rjs, char *key, char *value) {
|
||||
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
|
||||
rjson_write(rjs, ",");
|
||||
}
|
||||
rjson_write(rjs, "\"");
|
||||
rjson_write(rjs, key);
|
||||
rjson_write(rjs, "\":\"");
|
||||
char *value_str = (char *)rmalloc(strlen(value) + 4096);
|
||||
rstraddslashes(value, value_str);
|
||||
rjson_write(rjs, value_str);
|
||||
free(value_str);
|
||||
rjson_write(rjs, "\"");
|
||||
}
|
||||
|
||||
void rjson_kv_int(rjson_t *rjs, char *key, ulonglong value) {
|
||||
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
|
||||
rjson_write(rjs, ",");
|
||||
}
|
||||
rjson_write(rjs, "\"");
|
||||
rjson_write(rjs, key);
|
||||
rjson_write(rjs, "\":");
|
||||
char value_str[100] = {0};
|
||||
sprintf(value_str, "%lld", value);
|
||||
rjson_write(rjs, value_str);
|
||||
}
|
||||
void rjson_kv_number(rjson_t *rjs, char *key, ulonglong value) {
|
||||
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
|
||||
rjson_write(rjs, ",");
|
||||
}
|
||||
rjson_write(rjs, "\"");
|
||||
rjson_write(rjs, key);
|
||||
rjson_write(rjs, "\":");
|
||||
rjson_write(rjs, "\"");
|
||||
|
||||
rjson_write(rjs, sbuf(rformat_number(value)));
|
||||
rjson_write(rjs, "\"");
|
||||
}
|
||||
|
||||
void rjson_kv_bool(rjson_t *rjs, char *key, int value) {
|
||||
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
|
||||
rjson_write(rjs, ",");
|
||||
}
|
||||
rjson_write(rjs, "\"");
|
||||
rjson_write(rjs, key);
|
||||
rjson_write(rjs, "\":");
|
||||
rjson_write(rjs, value > 0 ? "true" : "false");
|
||||
}
|
||||
|
||||
void rjson_kv_duration(rjson_t *rjs, char *key, nsecs_t value) {
|
||||
if (rjs->length && !rstrendswith(rjs->content, "{") && !rstrendswith(rjs->content, "[")) {
|
||||
rjson_write(rjs, ",");
|
||||
}
|
||||
rjson_write(rjs, "\"");
|
||||
rjson_write(rjs, key);
|
||||
rjson_write(rjs, "\":");
|
||||
rjson_write(rjs, "\"");
|
||||
|
||||
rjson_write(rjs, sbuf(format_time(value)));
|
||||
rjson_write(rjs, "\"");
|
||||
}
|
||||
void rjson_free(rjson_t *rsj) {
|
||||
free(rsj->content);
|
||||
free(rsj);
|
||||
}
|
||||
|
||||
void rjson_key(rjson_t *rsj, char *key) {
|
||||
rjson_write(rsj, "\"");
|
||||
rjson_write(rsj, key);
|
||||
rjson_write(rsj, "\":");
|
||||
}
|
||||
#endif
|
12
rkeytable.c
Normal file
12
rkeytable.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include "rkeytable.h"
|
||||
#include "rtest.h"
|
||||
#include "rstring.h"
|
||||
|
||||
int main() {
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
char *key = rgenerate_key();
|
||||
rkset(key, "tast");
|
||||
rasserts(!strcmp(rkget(key), "tast"));
|
||||
}
|
||||
}
|
68
rkeytable.h
Normal file
68
rkeytable.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef RKEYTABLE_H
|
||||
#define RKEYTABLE_H
|
||||
/*
|
||||
DERIVED FROM HASH TABLE K&R
|
||||
*/
|
||||
#include "rmalloc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct rnklist {
|
||||
struct rnklist *next;
|
||||
struct rnklist *last;
|
||||
char *name;
|
||||
char *defn;
|
||||
} rnklist;
|
||||
|
||||
static rnklist *rkeytab = NULL;
|
||||
|
||||
rnklist *rlkget(char *s) {
|
||||
rnklist *np;
|
||||
for (np = rkeytab; np != NULL; np = np->next)
|
||||
if (strcmp(s, np->name) == 0)
|
||||
return np; // Found
|
||||
return NULL; // Not found
|
||||
}
|
||||
|
||||
char *rkget(char *s) {
|
||||
rnklist *np = rlkget(s);
|
||||
return np ? np->defn : NULL;
|
||||
}
|
||||
|
||||
rnklist *rkset(char *name, char *defn) {
|
||||
rnklist *np;
|
||||
if ((np = (rlkget(name))) == NULL) { // Not found
|
||||
np = (rnklist *)malloc(sizeof(rnklist));
|
||||
np->name = strdup(name);
|
||||
np->next = NULL;
|
||||
np->last = NULL;
|
||||
|
||||
if (defn) {
|
||||
np->defn = strdup(defn);
|
||||
} else {
|
||||
np->defn = NULL;
|
||||
}
|
||||
|
||||
if (rkeytab == NULL) {
|
||||
rkeytab = np;
|
||||
rkeytab->last = np;
|
||||
} else {
|
||||
if (rkeytab->last)
|
||||
rkeytab->last->next = np;
|
||||
|
||||
rkeytab->last = np;
|
||||
}
|
||||
} else {
|
||||
if (np->defn)
|
||||
free((void *)np->defn);
|
||||
if (defn) {
|
||||
np->defn = strdup(defn);
|
||||
} else {
|
||||
np->defn = NULL;
|
||||
}
|
||||
}
|
||||
return np;
|
||||
}
|
||||
#endif
|
189
rlexer.c
Normal file
189
rlexer.c
Normal file
@ -0,0 +1,189 @@
|
||||
#include "rlexer.h"
|
||||
#include "rio.h"
|
||||
#include "rtest.h"
|
||||
|
||||
void test_lexer() {
|
||||
rtest_banner("Lexer");
|
||||
|
||||
rlex("123"
|
||||
"-123 "
|
||||
"123.22.123.33"
|
||||
"-123.33"
|
||||
"abc "
|
||||
"_abc "
|
||||
"abc_ "
|
||||
"a_a"
|
||||
"\"string content 123\""
|
||||
"\"!@#$%^& *()-+\""
|
||||
"\"ab\\tc\\n\\\"\\r\""
|
||||
"--++-+/*<>!@#$%^&*(){}?[]"
|
||||
"\n"
|
||||
"()");
|
||||
rtest_banner("Number");
|
||||
rtoken_t token = rlex_next();
|
||||
rtest_assert(token.type == RT_NUMBER);
|
||||
rtest_assert(!strcmp(token.value, "123"));
|
||||
rtest_banner("Negative number");
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_NUMBER);
|
||||
rtest_assert(!strcmp(token.value, "-123"));
|
||||
rtest_banner("Decimal Number");
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_NUMBER);
|
||||
rtest_assert(!strcmp(token.value, "123.22"));
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_PUNCT);
|
||||
rtest_assert(!strcmp(token.value, "."));
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_NUMBER);
|
||||
rtest_assert(!strcmp(token.value, "123.33"));
|
||||
rtest_banner("Decimal Negative number");
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_NUMBER);
|
||||
rtest_assert(!strcmp(token.value, "-123.33"));
|
||||
rtest_banner("Symbol");
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_SYMBOL);
|
||||
rtest_assert(!strcmp(token.value, "abc"));
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_SYMBOL);
|
||||
rtest_assert(!strcmp(token.value, "_abc"));
|
||||
token = rlex_next();
|
||||
|
||||
rtest_assert(token.type == RT_SYMBOL);
|
||||
rtest_assert(!strcmp(token.value, "abc_"));
|
||||
|
||||
token = rlex_next();
|
||||
|
||||
rtest_assert(token.type == RT_SYMBOL);
|
||||
rtest_assert(!strcmp(token.value, "a_a"));
|
||||
rtest_banner("String");
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_STRING);
|
||||
rtest_assert(!strcmp(token.value, "string content 123"));
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_STRING);
|
||||
rtest_assert(!strcmp(token.value, "!@#$\%^& *()-+"));
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_STRING);
|
||||
rtest_assert(!strcmp(token.value, "ab\tc\n\"\r"));
|
||||
|
||||
rtest_banner("Operator");
|
||||
token = rlex_next();
|
||||
|
||||
rtest_assert(token.type == RT_OPERATOR);
|
||||
rtest_assert(!strcmp(token.value, "--++-+/*<>"));
|
||||
|
||||
rtest_banner("Punct") token = rlex_next();
|
||||
rtest_assert(token.type == RT_PUNCT);
|
||||
rtest_assert(!strcmp(token.value, "!@#$%^"));
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_OPERATOR);
|
||||
rtest_assert(!strcmp(token.value, "&*"));
|
||||
|
||||
rtest_banner("Grouping");
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_BRACE_OPEN);
|
||||
rassert(!strcmp(token.value, "("));
|
||||
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_BRACE_CLOSE);
|
||||
rassert(!strcmp(token.value, ")"));
|
||||
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_CURLY_BRACE_OPEN);
|
||||
rassert(!strcmp(token.value, "{"));
|
||||
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_CURLY_BRACE_CLOSE);
|
||||
rassert(!strcmp(token.value, "}"));
|
||||
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_PUNCT);
|
||||
rassert(!strcmp(token.value, "?"));
|
||||
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_BRACKET_OPEN);
|
||||
rassert(!strcmp(token.value, "["));
|
||||
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_BRACKET_CLOSE);
|
||||
rassert(!strcmp(token.value, "]"));
|
||||
|
||||
rtest_banner("Line number");
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_BRACE_OPEN);
|
||||
rassert(!strcmp(token.value, "("));
|
||||
rassert(token.line == 2);
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_BRACE_CLOSE);
|
||||
rassert(!strcmp(token.value, ")"));
|
||||
rassert(token.line == 2);
|
||||
|
||||
rtest_banner("EOF");
|
||||
token = rlex_next();
|
||||
rtest_assert(token.type == RT_EOF);
|
||||
rtest_assert(!strcmp(token.value, "eof"));
|
||||
rtest_assert(token.line == 2);
|
||||
}
|
||||
|
||||
void test_formatter() {
|
||||
rtest_banner("Formatter");
|
||||
char *formatted = rlex_format("{123{345{678}}}");
|
||||
char *expected_curly_braces = "{\n"
|
||||
" 123\n"
|
||||
" {\n"
|
||||
" 345\n"
|
||||
" {\n"
|
||||
" 678\n"
|
||||
" }\n \n"
|
||||
" }\n \n"
|
||||
"}\n";
|
||||
rtest_assert(!strcmp(formatted, expected_curly_braces));
|
||||
free(formatted);
|
||||
formatted = rlex_format("\"123\",66,true,(1,2,3)");
|
||||
char *expected_comma = "\"123\", 66, true, (1, 2, 3)";
|
||||
|
||||
rtest_assert(!strcmp(formatted, expected_comma));
|
||||
free(formatted);
|
||||
|
||||
formatted = rlex_format("lala lolo");
|
||||
char *expected_new_lines1 = "lala\nlolo";
|
||||
rtest_assert(!strcmp(formatted, expected_new_lines1));
|
||||
free(formatted);
|
||||
|
||||
formatted = rlex_format("lala=lolo");
|
||||
char *expected_new_lines2 = "lala = lolo";
|
||||
rtest_assert(!strcmp(formatted, expected_new_lines2));
|
||||
free(formatted);
|
||||
|
||||
formatted = rlex_format("lala+lolo=(1,2,3)");
|
||||
char *expected_new_lines3 = "lala + lolo = (1, 2, 3)";
|
||||
rtest_assert(!strcmp(formatted, expected_new_lines3));
|
||||
free(formatted);
|
||||
|
||||
formatted = rlex_format("lala+lolo=(1,2,3) little.test=(4,5,6)");
|
||||
char *expected_new_lines4 = "lala + lolo = (1, 2, 3)\nlittle.test = (4, 5, 6)";
|
||||
rtest_assert(!strcmp(formatted, expected_new_lines4));
|
||||
free(formatted);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc == 1) {
|
||||
test_formatter();
|
||||
test_lexer();
|
||||
return rtest_end("");
|
||||
} else {
|
||||
if (!rfile_exists(argv[1])) {
|
||||
rassert(false && "File does not exist.");
|
||||
}
|
||||
unsigned int length = rfile_size(argv[1]);
|
||||
char content[length + 1];
|
||||
|
||||
length = rfile_readb(argv[1], content, length);
|
||||
|
||||
content[length] = 0;
|
||||
char *formatted = rlex_format(content);
|
||||
printf("%s", formatted);
|
||||
}
|
||||
}
|
370
rlexer.h
Normal file
370
rlexer.h
Normal file
@ -0,0 +1,370 @@
|
||||
#ifndef RLEXER_H
|
||||
#define RLEXER_H
|
||||
#include "rmalloc.h"
|
||||
#include "rstring.h"
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define RTOKEN_VALUE_SIZE 1024
|
||||
|
||||
typedef enum rtoken_type_t {
|
||||
RT_UNKNOWN = 0,
|
||||
RT_SYMBOL,
|
||||
RT_NUMBER,
|
||||
RT_STRING,
|
||||
RT_PUNCT,
|
||||
RT_OPERATOR,
|
||||
RT_EOF = 10,
|
||||
RT_BRACE_OPEN,
|
||||
RT_CURLY_BRACE_OPEN,
|
||||
RT_BRACKET_OPEN,
|
||||
RT_BRACE_CLOSE,
|
||||
RT_CURLY_BRACE_CLOSE,
|
||||
RT_BRACKET_CLOSE
|
||||
} rtoken_type_t;
|
||||
|
||||
typedef struct rtoken_t {
|
||||
rtoken_type_t type;
|
||||
char value[RTOKEN_VALUE_SIZE];
|
||||
unsigned int line;
|
||||
unsigned int col;
|
||||
} rtoken_t;
|
||||
|
||||
static char *_content;
|
||||
static unsigned int _content_ptr;
|
||||
static unsigned int _content_line;
|
||||
static unsigned int _content_col;
|
||||
|
||||
static int isgroupingchar(char c) {
|
||||
return (c == '{' || c == '}' || c == '(' || c == ')' || c == '[' || c == ']' || c == '"' || c == '\'');
|
||||
}
|
||||
|
||||
static int isoperator(char c) {
|
||||
return (c == '+' || c == '-' || c == '/' || c == '*' || c == '=' || c == '>' || c == '<' || c == '|' || c == '&');
|
||||
}
|
||||
|
||||
static rtoken_t rtoken_new() {
|
||||
rtoken_t token;
|
||||
memset(&token, 0, sizeof(token));
|
||||
token.type = RT_UNKNOWN;
|
||||
return token;
|
||||
}
|
||||
|
||||
rtoken_t rlex_number() {
|
||||
rtoken_t token = rtoken_new();
|
||||
token.col = _content_col;
|
||||
token.line = _content_line;
|
||||
bool first_char = true;
|
||||
int dot_count = 0;
|
||||
char c;
|
||||
while (isdigit(c = _content[_content_ptr]) || (first_char && _content[_content_ptr] == '-') ||
|
||||
(dot_count == 0 && _content[_content_ptr] == '.')) {
|
||||
if (c == '.')
|
||||
dot_count++;
|
||||
first_char = false;
|
||||
char chars[] = {c, 0};
|
||||
strcat(token.value, chars);
|
||||
_content_ptr++;
|
||||
_content_col++;
|
||||
}
|
||||
token.type = RT_NUMBER;
|
||||
return token;
|
||||
}
|
||||
|
||||
static rtoken_t rlex_symbol() {
|
||||
rtoken_t token = rtoken_new();
|
||||
|
||||
token.col = _content_col;
|
||||
token.line = _content_line;
|
||||
char c;
|
||||
while (isalpha(_content[_content_ptr]) || _content[_content_ptr] == '_') {
|
||||
c = _content[_content_ptr];
|
||||
char chars[] = {c, 0};
|
||||
strcat(token.value, chars);
|
||||
_content_ptr++;
|
||||
_content_col++;
|
||||
}
|
||||
token.type = RT_SYMBOL;
|
||||
return token;
|
||||
}
|
||||
|
||||
static rtoken_t rlex_operator() {
|
||||
|
||||
rtoken_t token = rtoken_new();
|
||||
|
||||
token.col = _content_col;
|
||||
token.line = _content_line;
|
||||
char c;
|
||||
bool is_first = true;
|
||||
while (isoperator(_content[_content_ptr])) {
|
||||
if (!is_first) {
|
||||
if (_content[_content_ptr - 1] == '=' && _content[_content_ptr] == '-') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
c = _content[_content_ptr];
|
||||
char chars[] = {c, 0};
|
||||
strcat(token.value, chars);
|
||||
_content_ptr++;
|
||||
_content_col++;
|
||||
is_first = false;
|
||||
}
|
||||
token.type = RT_OPERATOR;
|
||||
return token;
|
||||
}
|
||||
|
||||
static rtoken_t rlex_punct() {
|
||||
|
||||
rtoken_t token = rtoken_new();
|
||||
|
||||
token.col = _content_col;
|
||||
token.line = _content_line;
|
||||
char c;
|
||||
bool is_first = true;
|
||||
while (ispunct(_content[_content_ptr])) {
|
||||
if (!is_first) {
|
||||
if (_content[_content_ptr] == '"') {
|
||||
break;
|
||||
}
|
||||
if (_content[_content_ptr] == '\'') {
|
||||
break;
|
||||
}
|
||||
if (isgroupingchar(_content[_content_ptr])) {
|
||||
break;
|
||||
}
|
||||
if (isoperator(_content[_content_ptr])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
c = _content[_content_ptr];
|
||||
char chars[] = {c, 0};
|
||||
strcat(token.value, chars);
|
||||
_content_ptr++;
|
||||
_content_col++;
|
||||
is_first = false;
|
||||
}
|
||||
token.type = RT_PUNCT;
|
||||
return token;
|
||||
}
|
||||
|
||||
static rtoken_t rlex_string() {
|
||||
rtoken_t token = rtoken_new();
|
||||
char c;
|
||||
token.col = _content_col;
|
||||
token.line = _content_line;
|
||||
char str_chr = _content[_content_ptr];
|
||||
_content_ptr++;
|
||||
while (_content[_content_ptr] != str_chr) {
|
||||
c = _content[_content_ptr];
|
||||
if (c == '\\') {
|
||||
_content_ptr++;
|
||||
c = _content[_content_ptr];
|
||||
if (c == 'n') {
|
||||
c = '\n';
|
||||
} else if (c == 'r') {
|
||||
c = '\r';
|
||||
} else if (c == 't') {
|
||||
c = '\t';
|
||||
} else if (c == str_chr) {
|
||||
c = str_chr;
|
||||
}
|
||||
|
||||
_content_col++;
|
||||
}
|
||||
char chars[] = {c, 0};
|
||||
strcat(token.value, chars);
|
||||
_content_ptr++;
|
||||
_content_col++;
|
||||
}
|
||||
_content_ptr++;
|
||||
token.type = RT_STRING;
|
||||
return token;
|
||||
}
|
||||
|
||||
void rlex(char *content) {
|
||||
_content = content;
|
||||
_content_ptr = 0;
|
||||
_content_col = 1;
|
||||
_content_line = 1;
|
||||
}
|
||||
|
||||
static void rlex_repeat_str(char *dest, char *src, unsigned int times) {
|
||||
for (size_t i = 0; i < times; i++) {
|
||||
strcat(dest, src);
|
||||
}
|
||||
}
|
||||
|
||||
rtoken_t rtoken_create(rtoken_type_t type, char *value) {
|
||||
rtoken_t token = rtoken_new();
|
||||
token.type = type;
|
||||
token.col = _content_col;
|
||||
token.line = _content_line;
|
||||
strcpy(token.value, value);
|
||||
return token;
|
||||
}
|
||||
|
||||
rtoken_t rlex_next() {
|
||||
while (true) {
|
||||
|
||||
_content_col++;
|
||||
|
||||
if (_content[_content_ptr] == 0) {
|
||||
return rtoken_create(RT_EOF, "eof");
|
||||
} else if (_content[_content_ptr] == '\n') {
|
||||
_content_line++;
|
||||
_content_col = 1;
|
||||
_content_ptr++;
|
||||
} else if (isspace(_content[_content_ptr])) {
|
||||
_content_ptr++;
|
||||
} else if (isdigit(_content[_content_ptr]) || (_content[_content_ptr] == '-' && isdigit(_content[_content_ptr + 1]))) {
|
||||
return rlex_number();
|
||||
} else if (isalpha(_content[_content_ptr]) || _content[_content_ptr] == '_') {
|
||||
return rlex_symbol();
|
||||
} else if (_content[_content_ptr] == '"' || _content[_content_ptr] == '\'') {
|
||||
return rlex_string();
|
||||
} else if (isoperator(_content[_content_ptr])) {
|
||||
return rlex_operator();
|
||||
} else if (ispunct(_content[_content_ptr])) {
|
||||
if (_content[_content_ptr] == '{') {
|
||||
|
||||
_content_ptr++;
|
||||
return rtoken_create(RT_CURLY_BRACE_OPEN, "{");
|
||||
}
|
||||
if (_content[_content_ptr] == '}') {
|
||||
|
||||
_content_ptr++;
|
||||
return rtoken_create(RT_CURLY_BRACE_CLOSE, "}");
|
||||
}
|
||||
if (_content[_content_ptr] == '(') {
|
||||
|
||||
_content_ptr++;
|
||||
return rtoken_create(RT_BRACE_OPEN, "(");
|
||||
}
|
||||
if (_content[_content_ptr] == ')') {
|
||||
|
||||
_content_ptr++;
|
||||
return rtoken_create(RT_BRACE_CLOSE, ")");
|
||||
}
|
||||
if (_content[_content_ptr] == '[') {
|
||||
|
||||
_content_ptr++;
|
||||
return rtoken_create(RT_BRACKET_OPEN, "[");
|
||||
}
|
||||
if (_content[_content_ptr] == ']') {
|
||||
|
||||
_content_ptr++;
|
||||
return rtoken_create(RT_BRACKET_CLOSE, "]");
|
||||
}
|
||||
return rlex_punct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *rlex_format(char *content) {
|
||||
rlex(content);
|
||||
char *result = (char *)malloc(strlen(content) + 4096);
|
||||
result[0] = 0;
|
||||
unsigned int tab_index = 0;
|
||||
char *tab_chars = " ";
|
||||
unsigned int col = 0;
|
||||
rtoken_t token_previous;
|
||||
token_previous.value[0] = 0;
|
||||
token_previous.type = RT_UNKNOWN;
|
||||
while (true) {
|
||||
rtoken_t token = rlex_next();
|
||||
if (token.type == RT_EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
// col = strlen(token.value);
|
||||
|
||||
if (col == 0) {
|
||||
rlex_repeat_str(result, tab_chars, tab_index);
|
||||
// col = strlen(token.value);// strlen(tab_chars) * tab_index;
|
||||
}
|
||||
|
||||
if (token.type == RT_STRING) {
|
||||
strcat(result, "\"");
|
||||
|
||||
char string_with_slashes[strlen(token.value) * 2 + 1];
|
||||
rstraddslashes(token.value, string_with_slashes);
|
||||
strcat(result, string_with_slashes);
|
||||
|
||||
strcat(result, "\"");
|
||||
// col+= strlen(token.value) + 2;
|
||||
// printf("\n");
|
||||
// printf("<<<%s>>>\n",token.value);
|
||||
|
||||
memcpy(&token_previous, &token, sizeof(token));
|
||||
continue;
|
||||
}
|
||||
if (!(strcmp(token.value, "{"))) {
|
||||
if (col != 0) {
|
||||
strcat(result, "\n");
|
||||
rlex_repeat_str(result, " ", tab_index);
|
||||
}
|
||||
strcat(result, token.value);
|
||||
|
||||
tab_index++;
|
||||
|
||||
strcat(result, "\n");
|
||||
|
||||
col = 0;
|
||||
|
||||
memcpy(&token_previous, &token, sizeof(token));
|
||||
continue;
|
||||
} else if (!(strcmp(token.value, "}"))) {
|
||||
unsigned int tab_indexed = 0;
|
||||
if (tab_index)
|
||||
tab_index--;
|
||||
strcat(result, "\n");
|
||||
|
||||
rlex_repeat_str(result, tab_chars, tab_index);
|
||||
tab_indexed++;
|
||||
|
||||
strcat(result, token.value);
|
||||
strcat(result, "\n");
|
||||
col = 0;
|
||||
|
||||
memcpy(&token_previous, &token, sizeof(token));
|
||||
continue;
|
||||
}
|
||||
if ((token_previous.type == RT_SYMBOL && token.type == RT_NUMBER) ||
|
||||
(token_previous.type == RT_NUMBER && token.type == RT_SYMBOL) || (token_previous.type == RT_PUNCT && token.type == RT_SYMBOL) ||
|
||||
(token_previous.type == RT_BRACE_CLOSE && token.type == RT_SYMBOL) ||
|
||||
(token_previous.type == RT_SYMBOL && token.type == RT_SYMBOL)) {
|
||||
if (token_previous.value[0] != ',' && token_previous.value[0] != '.') {
|
||||
if (token.type != RT_OPERATOR && token.value[0] != '.') {
|
||||
strcat(result, "\n");
|
||||
rlex_repeat_str(result, tab_chars, tab_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (token.type == RT_OPERATOR) {
|
||||
strcat(result, " ");
|
||||
}
|
||||
if (token.type == RT_STRING) {
|
||||
strcat(result, "\"");
|
||||
}
|
||||
strcat(result, token.value);
|
||||
if (token.type == RT_STRING) {
|
||||
strcat(result, "\"");
|
||||
}
|
||||
|
||||
if (token.type == RT_OPERATOR) {
|
||||
strcat(result, " ");
|
||||
}
|
||||
if (!strcmp(token.value, ",")) {
|
||||
strcat(result, " ");
|
||||
}
|
||||
col += strlen(token.value);
|
||||
memcpy(&token_previous, &token, sizeof(token));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
3
rlib.c
Normal file
3
rlib.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include "rlib.h"
|
||||
|
||||
int main(int argc, char **argv) { return rlib_main(argc, argv); }
|
8450
rlibrlibso.c
Normal file
8450
rlibrlibso.c
Normal file
File diff suppressed because it is too large
Load Diff
18
rlibso.c
Normal file
18
rlibso.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void *rmalloc(size_t);
|
||||
void *rfree(void *);
|
||||
int rtest_end(char *);
|
||||
void rprintgf(FILE *f, char *format, ...);
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
void *data = rmalloc(5000);
|
||||
memset(data, 0, 5000);
|
||||
rfree(data);
|
||||
}
|
||||
rprintgf(stdout, "Hello from .so library!");
|
||||
return rtest_end("");
|
||||
}
|
156
rliza.c
Normal file
156
rliza.c
Normal file
@ -0,0 +1,156 @@
|
||||
#define RMALLOC_OVERRIDE 1
|
||||
#include "rtest.h"
|
||||
#include "rliza.h"
|
||||
#include "rio.h"
|
||||
#include "rbench.h"
|
||||
void performance_test() {
|
||||
size_t size = rfile_size("resources/large.json");
|
||||
char *data = malloc(size + 1);
|
||||
rfile_readb("resources/large.json", data, size);
|
||||
data[size] = 0;
|
||||
RBENCH(1, {
|
||||
int length = rliza_validate(data);
|
||||
(void)length;
|
||||
});
|
||||
free(data);
|
||||
}
|
||||
|
||||
int main() {
|
||||
rtest_banner("rliza");
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
char *long_data =
|
||||
"{\"event\": \"execute\", \"query\": \"update session set bytes = ?, ex= ? where key = ?\", \"params\": [\"{\\\"created\\\": "
|
||||
"1731034143, \\\"session\\\": {\\\"req_id\\\": 1, \\\"much_data\\\": "
|
||||
"\\\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\\"}}\", null, \"AIOHTTP_SESSION_5a2510809b85492b8f14e8d3e2f11da3\"]}";
|
||||
rassert(rliza_validate(long_data));
|
||||
rliza_t *a = rliza_loads(&long_data);
|
||||
rliza_free(a);
|
||||
}
|
||||
char *nested_obj = "{\"test\":123,\"test2\":{\"test3\":123}}";
|
||||
rliza_t *a = rliza_loads(&nested_obj);
|
||||
char *b = rliza_dumps(a);
|
||||
printf("%s\n", b);
|
||||
// rliza_dumpss(a);
|
||||
rliza_free(a);
|
||||
free(b);
|
||||
|
||||
rtest_banner("performance test");
|
||||
performance_test();
|
||||
rtest_banner("serialize/deserialize");
|
||||
char *json_content = "{\"error\":\"not \\\"aaa\\\" "
|
||||
"found\",\"rows\":[{\"a\":true},{\"b\":false},{\"c\":null},[1,23],[1,23],[1,23,true,false,5.5,5.505,6.1]]}";
|
||||
|
||||
rassert(rliza_validate("{\"error\":\"not \\\"aaa\\\" found\",\"rows\":[[1,23],[1,23],[1,23,true,false,5.5,5.505]]}"));
|
||||
rassert(!rliza_validate("{\"error\":\"not \\\"aaa\\\" found\",\"rows\":[[1,23],[1,23],[1,23,true,false,5.5,5.505,6.]]}"));
|
||||
rassert(!rliza_validate("{\"error\":\"not \\\"aaa\\\" found\",\"rows\""));
|
||||
rassert(!rliza_validate("{\"error\":\"not \\\"aaa\\\" found\",\"rows\":["));
|
||||
rassert(!rliza_validate("{\"error\":\"not \\\"aaa\\\" found\",\"rows"));
|
||||
rassert(!rliza_validate("{\"error\":\"not \\\"aaa\\\" found\",\""));
|
||||
rassert(!rliza_validate("{\"error\":\"not \\\"aaa\\\" found\","));
|
||||
|
||||
char *double_content = "{}{}[]";
|
||||
free(rliza_loads(&double_content));
|
||||
rassert(!strcmp(double_content, "{}[]"));
|
||||
free(rliza_loads(&double_content));
|
||||
rassert(!strcmp(double_content, "[]"));
|
||||
char *error_content = "{}*{}";
|
||||
free(rliza_loads(&error_content));
|
||||
rassert(!strcmp(error_content, "*{}"));
|
||||
rliza_loads(&error_content);
|
||||
rassert(!strcmp(error_content, "*{}"));
|
||||
|
||||
rassert(rliza_validate("{}"));
|
||||
rassert(!rliza_validate("{"));
|
||||
rassert(!rliza_validate("}"));
|
||||
|
||||
rassert(!rliza_validate("["));
|
||||
rassert(!rliza_validate("]"));
|
||||
rassert(!rliza_validate("\\"));
|
||||
rassert(!rliza_validate("*"));
|
||||
rassert(!rliza_validate("!"));
|
||||
char *json_contentp = json_content;
|
||||
rliza_t *to_object = rliza_loads(&json_contentp);
|
||||
char *to_string = (char *)rliza_dumps(to_object);
|
||||
rassert(!strcmp(to_string, json_content));
|
||||
printf("\n<%s>\n", to_string);
|
||||
printf("<%s>\n", json_content);
|
||||
free(to_string);
|
||||
|
||||
rliza_free(to_object);
|
||||
|
||||
// rliza_free(to_object);
|
||||
rtest_banner("manually building new object");
|
||||
rliza_t *rliza = rliza_new(RLIZA_OBJECT);
|
||||
rliza_set_integer(rliza, "a", 1);
|
||||
rliza_set_integer(rliza, "b", 2);
|
||||
rliza_set_integer(rliza, "c", 3);
|
||||
rliza_set_integer(rliza, "d", 4);
|
||||
rliza_set_integer(rliza, "e", 5);
|
||||
rliza_set_integer(rliza, "f", 6);
|
||||
rliza_set_string(rliza, "str1", "str1value");
|
||||
|
||||
rliza_set_null(rliza, "q");
|
||||
|
||||
char *original_content = rliza_dumps(rliza);
|
||||
printf("1:%s\n", original_content);
|
||||
char *content = original_content;
|
||||
|
||||
printf("2:%s %d\n", content, content[strlen((char *)content)] == 0);
|
||||
|
||||
rliza_t *rliza2 = rliza_loads(&content);
|
||||
|
||||
printf("HAAAh\n");
|
||||
char *content2 = rliza_dumps(rliza2);
|
||||
printf("HAAAh\n");
|
||||
content = original_content;
|
||||
rassert(!(strcmp((char *)content,
|
||||
(char *)content2))); // strcmp(content,content2);
|
||||
char *content2p = original_content;
|
||||
content = original_content;
|
||||
rliza_t *rliza3 = rliza_loads((char **)&content2p);
|
||||
char *content3 = rliza_dumps(rliza2);
|
||||
|
||||
rtest_banner("compare several serilizations. Should be equal.\n");
|
||||
content = original_content;
|
||||
printf("content1:<%s>\n", content);
|
||||
printf("content2:<%s>\n", content2);
|
||||
printf("content3:<%s>\n", content3);
|
||||
rassert(!strncmp(content2, content3, strlen((char *)content2)));
|
||||
rassert(!strncmp(content, content2, strlen((char *)content)));
|
||||
rliza_free(rliza2);
|
||||
rliza_free(rliza3);
|
||||
free(original_content);
|
||||
free(content2);
|
||||
free(content3);
|
||||
printf("Coalesce %s\n", (char *)rliza_coalesce(rliza_get_string(rliza, "a"), "#1"));
|
||||
printf("Coalesce %s\n", (char *)rliza_coalesce(rliza_get_string(rliza, "b"), "#2"));
|
||||
|
||||
rliza_free(rliza);
|
||||
|
||||
return rtest_end("");
|
||||
}
|
775
rliza.h
Normal file
775
rliza.h
Normal file
@ -0,0 +1,775 @@
|
||||
#ifndef RLIZA_H
|
||||
#define RLIZA_H
|
||||
#include "rbuffer.h"
|
||||
#include "rmalloc.h"
|
||||
#include "rstring.h"
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum rliza_type_t {
|
||||
RLIZA_STRING = 's',
|
||||
RLIZA_BOOLEAN = 'b',
|
||||
RLIZA_NUMBER = 'n',
|
||||
RLIZA_OBJECT = 'o',
|
||||
RLIZA_ARRAY = 'a',
|
||||
RLIZA_NULL = 0,
|
||||
RLIZA_KEY = 'k',
|
||||
RLIZA_INTEGER = 'i'
|
||||
} rliza_type_t;
|
||||
|
||||
typedef struct rliza_t {
|
||||
rliza_type_t type;
|
||||
struct rliza_t *value;
|
||||
char *key;
|
||||
union {
|
||||
char *string;
|
||||
bool boolean;
|
||||
double number;
|
||||
struct rliza_t **map;
|
||||
long long integer;
|
||||
} content;
|
||||
unsigned int count;
|
||||
char *(*get_string)(struct rliza_t *, char *);
|
||||
long long (*get_integer)(struct rliza_t *, char *);
|
||||
double (*get_number)(struct rliza_t *, char *);
|
||||
bool (*get_boolean)(struct rliza_t *, char *);
|
||||
struct rliza_t *(*get_array)(struct rliza_t *, char *);
|
||||
struct rliza_t *(*get_object)(struct rliza_t *, char *);
|
||||
void (*set_string)(struct rliza_t *, char *, char *);
|
||||
void (*set_integer)(struct rliza_t *, char *, long long);
|
||||
void (*set_number)(struct rliza_t *, char *, double);
|
||||
void (*set_boolean)(struct rliza_t *, char *, bool);
|
||||
void (*set_array)(struct rliza_t *self, char *key, struct rliza_t *array);
|
||||
void (*set_object)(struct rliza_t *self, char *key, struct rliza_t *object);
|
||||
} rliza_t;
|
||||
|
||||
void rliza_free(rliza_t *rliza) {
|
||||
if (rliza->key) {
|
||||
free(rliza->key);
|
||||
rliza->key = NULL;
|
||||
}
|
||||
if (rliza->value) {
|
||||
rliza_free(rliza->value);
|
||||
rliza->value = NULL;
|
||||
}
|
||||
// if (rliza->content.array) {
|
||||
// printf("JAAAA\n");
|
||||
// }
|
||||
// if (rliza->content.object) {
|
||||
// rliza_free(rliza->content.object);
|
||||
// rliza->content.object = NULL;
|
||||
//}
|
||||
if (rliza->type == RLIZA_STRING) {
|
||||
if (rliza->content.string) {
|
||||
free(rliza->content.string);
|
||||
rliza->content.string = NULL;
|
||||
// else if (rliza->type == RLIZA_NUMBER) {
|
||||
// printf("STDring freed\n");
|
||||
}
|
||||
} else if (rliza->type == RLIZA_OBJECT || rliza->type == RLIZA_ARRAY) {
|
||||
|
||||
if (rliza->content.map) {
|
||||
for (unsigned int i = 0; i < rliza->count; i++) {
|
||||
rliza_free(rliza->content.map[i]);
|
||||
}
|
||||
free(rliza->content.map);
|
||||
}
|
||||
}
|
||||
// free(rliza->content.array);
|
||||
//}
|
||||
|
||||
free(rliza);
|
||||
}
|
||||
|
||||
rliza_t *rliza_new(rliza_type_t type);
|
||||
rliza_t *rliza_new_string(char *string);
|
||||
rliza_t *rliza_new_null();
|
||||
rliza_t *rliza_new_boolean(bool value);
|
||||
rliza_t *rliza_new_number(double value);
|
||||
rliza_t *rliza_new_integer(long long value);
|
||||
rliza_t *rliza_new_key_value(char *key, rliza_t *value);
|
||||
rliza_t *rliza_new_key_string(char *key, char *string);
|
||||
rliza_t *rliza_new_key_bool(char *key, bool value);
|
||||
rliza_t *rliza_new_key_number(char *key, double value);
|
||||
void rliza_push(rliza_t *self, rliza_t *obj);
|
||||
void rliza_push_object(rliza_t *self, rliza_t *object);
|
||||
void rliza_set_object(rliza_t *self, char *key, rliza_t *object);
|
||||
void rliza_set_string(rliza_t *self, char *key, char *string);
|
||||
void rliza_set_boolean(rliza_t *self, char *key, bool value);
|
||||
void rliza_set_number(rliza_t *self, char *key, double value);
|
||||
void rliza_set_integer(rliza_t *self, char *key, long long value);
|
||||
char *rliza_get_string(rliza_t *self, char *key);
|
||||
long long rliza_get_integer(rliza_t *self, char *key);
|
||||
double rliza_get_number(rliza_t *self, char *key);
|
||||
bool rliza_get_boolean(rliza_t *self, char *key);
|
||||
rliza_t *rliza_get_array(rliza_t *self, char *key);
|
||||
rliza_t *rliza_get_object(rliza_t *self, char *key);
|
||||
void rliza_set_array(rliza_t *self, char *key, rliza_t *array);
|
||||
|
||||
char *rliza_dumps(rliza_t *rliza);
|
||||
rliza_t *rliza_loads(char **content);
|
||||
rliza_t *_rliza_loads(char **content);
|
||||
|
||||
char *rliza_get_string(rliza_t *self, char *key) {
|
||||
for (unsigned int i = 0; i < self->count; i++) {
|
||||
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
|
||||
if (self->content.map[i]->type == RLIZA_STRING || self->content.map[i]->type == RLIZA_NULL) {
|
||||
return self->content.map[i]->content.string;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
long long rliza_get_integer(rliza_t *self, char *key) {
|
||||
for (unsigned int i = 0; i < self->count; i++) {
|
||||
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
|
||||
if (self->content.map[i]->type == RLIZA_INTEGER || self->content.map[i]->type == RLIZA_NULL) {
|
||||
return self->content.map[i]->content.integer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double rliza_get_number(rliza_t *self, char *key) {
|
||||
for (unsigned int i = 0; i < self->count; i++) {
|
||||
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
|
||||
if (self->content.map[i]->type == RLIZA_NUMBER || self->content.map[i]->type == RLIZA_NULL) {
|
||||
return self->content.map[i]->content.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool rliza_get_boolean(rliza_t *self, char *key) {
|
||||
for (unsigned int i = 0; i < self->count; i++) {
|
||||
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
|
||||
if (self->content.map[i]->type == RLIZA_BOOLEAN || self->content.map[i]->type == RLIZA_NULL) {
|
||||
return self->content.map[i]->content.boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
rliza_t *rliza_get_object(rliza_t *self, char *key) {
|
||||
for (unsigned int i = 0; i < self->count; i++) {
|
||||
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
|
||||
return self->content.map[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rliza_t *rliza_get_array(rliza_t *self, char *key) {
|
||||
for (unsigned int i = 0; i < self->count; i++) {
|
||||
if (self->content.map[i]->key != NULL && strcmp(self->content.map[i]->key, key) == 0) {
|
||||
if (self->content.map[i]->type == RLIZA_ARRAY || self->content.map[i]->type == RLIZA_NULL) {
|
||||
return self->content.map[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rliza_t *rliza_new_null() {
|
||||
rliza_t *rliza = rliza_new(RLIZA_NULL);
|
||||
return rliza;
|
||||
}
|
||||
rliza_t *rliza_new_string(char *string) {
|
||||
rliza_t *rliza = rliza_new(RLIZA_STRING);
|
||||
if (string == NULL) {
|
||||
rliza->type = RLIZA_NULL;
|
||||
rliza->content.string = NULL;
|
||||
return rliza;
|
||||
} else {
|
||||
rliza->content.string = strdup(string);
|
||||
}
|
||||
return rliza;
|
||||
}
|
||||
rliza_t *rliza_new_boolean(bool value) {
|
||||
rliza_t *rliza = rliza_new(RLIZA_BOOLEAN);
|
||||
rliza->content.boolean = value;
|
||||
return rliza;
|
||||
}
|
||||
|
||||
rliza_t *rliza_new_number(double value) {
|
||||
rliza_t *rliza = rliza_new(RLIZA_NUMBER);
|
||||
rliza->content.number = value;
|
||||
return rliza;
|
||||
}
|
||||
|
||||
rliza_t *rliza_new_integer(long long value) {
|
||||
rliza_t *rliza = rliza_new(RLIZA_INTEGER);
|
||||
rliza->content.integer = value;
|
||||
return rliza;
|
||||
}
|
||||
rliza_t *rliza_new_key_array(char *key) {
|
||||
rliza_t *rliza = rliza_new(RLIZA_ARRAY);
|
||||
rliza->key = strdup(key);
|
||||
return rliza;
|
||||
}
|
||||
|
||||
rliza_t *rliza_new_key_value(char *key, rliza_t *value) {
|
||||
rliza_t *rliza = rliza_new(RLIZA_OBJECT);
|
||||
if (key) {
|
||||
rliza->key = strdup(key);
|
||||
}
|
||||
rliza->value = value;
|
||||
return rliza;
|
||||
}
|
||||
|
||||
rliza_t *rliza_new_key_string(char *key, char *string) {
|
||||
rliza_t *rliza = rliza_new_key_value(key, rliza_new_string(string));
|
||||
return rliza;
|
||||
}
|
||||
rliza_t *rliza_new_key_bool(char *key, bool value) {
|
||||
rliza_t *rliza = rliza_new_key_value(key, rliza_new_boolean(value));
|
||||
return rliza;
|
||||
}
|
||||
rliza_t *rliza_new_key_number(char *key, double value) {
|
||||
rliza_t *rliza = rliza_new_key_value(key, rliza_new_number(value));
|
||||
return rliza;
|
||||
}
|
||||
|
||||
void rliza_set_null(rliza_t *self, char *key) {
|
||||
rliza_t *obj = rliza_get_object(self, key);
|
||||
if (!obj) {
|
||||
obj = rliza_new_null();
|
||||
obj->key = strdup(key);
|
||||
rliza_push_object(self, obj);
|
||||
}
|
||||
if (obj->type == RLIZA_OBJECT) {
|
||||
|
||||
rliza_free(obj->value);
|
||||
obj->value = NULL;
|
||||
} else if (obj->type == RLIZA_STRING) {
|
||||
if (obj->content.string)
|
||||
free(obj->content.string);
|
||||
obj->content.string = NULL;
|
||||
} else if (obj->type == RLIZA_ARRAY) {
|
||||
for (unsigned int i = 0; i < obj->count; i++) {
|
||||
rliza_free(obj->content.map[i]);
|
||||
}
|
||||
} else if (obj->type == RLIZA_NUMBER) {
|
||||
obj->content.number = 0;
|
||||
} else if (obj->type == RLIZA_INTEGER) {
|
||||
obj->content.integer = 0;
|
||||
}
|
||||
obj->type = RLIZA_NULL;
|
||||
}
|
||||
|
||||
rliza_t *rliza_duplicate(rliza_t *rliza) {
|
||||
if (!rliza)
|
||||
return NULL;
|
||||
char *str = rliza_dumps(rliza);
|
||||
char *strp = str;
|
||||
rliza_t *obj = rliza_loads(&strp);
|
||||
free(str);
|
||||
return obj;
|
||||
}
|
||||
|
||||
rliza_t *rliza_new_object(rliza_t *obj) {
|
||||
rliza_t *rliza = rliza_new(RLIZA_OBJECT);
|
||||
rliza->value = obj;
|
||||
return rliza;
|
||||
}
|
||||
void rliza_set_object(rliza_t *self, char *key, rliza_t *value) {
|
||||
rliza_t *obj = rliza_duplicate(value);
|
||||
obj->key = strdup(key);
|
||||
obj->type = RLIZA_OBJECT;
|
||||
rliza_push(self, obj);
|
||||
}
|
||||
|
||||
void rliza_set_string(rliza_t *self, char *key, char *string) {
|
||||
rliza_t *obj = rliza_get_object(self, key);
|
||||
|
||||
if (!obj) {
|
||||
obj = rliza_new_string(string);
|
||||
obj->key = strdup(key);
|
||||
obj->type = RLIZA_STRING;
|
||||
rliza_push_object(self, obj);
|
||||
} else {
|
||||
obj->content.string = strdup(string);
|
||||
}
|
||||
}
|
||||
|
||||
void rliza_set_array(rliza_t *self, char *key, rliza_t *array) {
|
||||
rliza_t *obj = rliza_get_object(self, key);
|
||||
if (obj)
|
||||
rliza_free(obj);
|
||||
if (array->key) {
|
||||
free(array->key);
|
||||
array->key = strdup(key);
|
||||
}
|
||||
rliza_push_object(self, array);
|
||||
}
|
||||
|
||||
void rliza_set_number(rliza_t *self, char *key, double value) {
|
||||
rliza_t *obj = rliza_get_object(self, key);
|
||||
if (!obj) {
|
||||
obj = rliza_new_number(value);
|
||||
obj->key = strdup(key);
|
||||
obj->type = RLIZA_NUMBER;
|
||||
rliza_push_object(self, obj);
|
||||
} else {
|
||||
obj->content.number = value;
|
||||
}
|
||||
}
|
||||
|
||||
void rliza_push_object(rliza_t *self, rliza_t *object) {
|
||||
self->content.map = realloc(self->content.map, (sizeof(rliza_t **)) * (self->count + 1));
|
||||
self->content.map[self->count] = object;
|
||||
self->count++;
|
||||
}
|
||||
void rliza_set_integer(rliza_t *self, char *key, long long value) {
|
||||
rliza_t *obj = rliza_get_object(self, key);
|
||||
if (!obj) {
|
||||
obj = rliza_new_integer(value);
|
||||
obj->key = strdup(key);
|
||||
obj->type = RLIZA_INTEGER;
|
||||
rliza_push_object(self, obj);
|
||||
} else {
|
||||
obj->content.integer = value;
|
||||
}
|
||||
}
|
||||
|
||||
void rliza_set_boolean(rliza_t *self, char *key, bool value) {
|
||||
rliza_t *obj = rliza_get_object(self, key);
|
||||
if (!obj) {
|
||||
obj = rliza_new_boolean(value);
|
||||
obj->key = strdup(key);
|
||||
obj->type = RLIZA_BOOLEAN;
|
||||
|
||||
rliza_push_object(self, obj);
|
||||
} else {
|
||||
obj->content.boolean = value;
|
||||
}
|
||||
}
|
||||
|
||||
rliza_t *rliza_new(rliza_type_t type) {
|
||||
rliza_t *rliza = (rliza_t *)calloc(1, sizeof(rliza_t));
|
||||
rliza->type = type;
|
||||
rliza->get_boolean = rliza_get_boolean;
|
||||
rliza->get_integer = rliza_get_integer;
|
||||
rliza->get_number = rliza_get_number;
|
||||
rliza->get_string = rliza_get_string;
|
||||
rliza->get_array = rliza_get_array;
|
||||
rliza->get_object = rliza_get_object;
|
||||
rliza->set_string = rliza_set_string;
|
||||
rliza->set_number = rliza_set_number;
|
||||
rliza->set_boolean = rliza_set_boolean;
|
||||
rliza->set_integer = rliza_set_integer;
|
||||
rliza->set_array = rliza_set_array;
|
||||
rliza->set_object = rliza_set_object;
|
||||
|
||||
return rliza;
|
||||
}
|
||||
|
||||
void *rliza_coalesce(void *result, void *default_value) {
|
||||
if (result == NULL)
|
||||
return default_value;
|
||||
return result;
|
||||
}
|
||||
|
||||
char *rliza_seek_string(char **content, char **options) {
|
||||
|
||||
while (**content == ' ' || **content == '\n' || **content == '\t' || **content == '\r') {
|
||||
(*content)++;
|
||||
}
|
||||
if (**content == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *option = NULL;
|
||||
unsigned int option_index = 0;
|
||||
|
||||
while (true) {
|
||||
option = options[option_index];
|
||||
if (option == NULL)
|
||||
break;
|
||||
option_index++;
|
||||
if (option[0] == 'd') {
|
||||
if (**content >= '0' && **content <= '9') {
|
||||
return (char *)*content;
|
||||
}
|
||||
} else if (!strncmp(option, *content, strlen(option))) {
|
||||
return (char *)*content;
|
||||
}
|
||||
}
|
||||
return *content;
|
||||
}
|
||||
|
||||
char *rliza_extract_quotes(char **content) {
|
||||
rbuffer_t *buffer = rbuffer_new(NULL, 0);
|
||||
assert(**content == '"');
|
||||
char previous = 0;
|
||||
while (true) {
|
||||
|
||||
(*content)++;
|
||||
if (!**content) {
|
||||
rbuffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (**content == '"' && previous != '\\') {
|
||||
break;
|
||||
}
|
||||
rbuffer_push(buffer, **content);
|
||||
previous = **content;
|
||||
}
|
||||
assert(**content == '"');
|
||||
(*content)++;
|
||||
rbuffer_push(buffer, 0);
|
||||
char *result = (char *)rbuffer_to_string(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
rliza_t *_rliza_loads(char **content) {
|
||||
static char *seek_for1[] = {"[", "{", "\"", "d", "true", "false", "null", NULL};
|
||||
char *token = (char *)rliza_seek_string(content, seek_for1);
|
||||
if (!token)
|
||||
return NULL;
|
||||
rliza_t *rliza = rliza_new(RLIZA_NULL);
|
||||
if (**content == '"') {
|
||||
char *extracted = rliza_extract_quotes(content);
|
||||
if (!extracted) {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
// char *extracted_with_slashes = (char *)malloc(strlen((char *)extracted) * 2 + 1);
|
||||
// rstraddslashes(extracted, extracted_with_slashes);
|
||||
rliza->type = RLIZA_STRING;
|
||||
rliza->content.string = extracted; // extracted_with_slashes; // extracted_without_slashes;
|
||||
// free(extracted);
|
||||
return rliza;
|
||||
} else if (**content == '{') {
|
||||
rliza->type = RLIZA_OBJECT;
|
||||
(*content)++;
|
||||
char *result = NULL;
|
||||
static char *seek_for2[] = {"\"", ",", "}", NULL};
|
||||
while ((result = (char *)rliza_seek_string(content, seek_for2)) != NULL && *result) {
|
||||
|
||||
if (!**content) {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
if (**content == ',') {
|
||||
(*content)++;
|
||||
if (!**content) {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
char *key = NULL;
|
||||
if (**content == '"') {
|
||||
key = rliza_extract_quotes((char **)content);
|
||||
if (!key || !*key) {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
char *escaped_key = (char *)malloc(strlen((char *)key) * 2 + 1);
|
||||
rstrstripslashes((char *)key, escaped_key);
|
||||
static char *seek_for3[] = {":", NULL};
|
||||
char *devider = rliza_seek_string(content, seek_for3);
|
||||
|
||||
if (!devider || !*devider) {
|
||||
free(escaped_key);
|
||||
free(key);
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
(*content)++;
|
||||
if (!**content) {
|
||||
free(key);
|
||||
free(escaped_key);
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
rliza_t *value = _rliza_loads(content);
|
||||
if (!value) {
|
||||
free(key);
|
||||
free(escaped_key);
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
if (value->key)
|
||||
free(value->key);
|
||||
value->key = escaped_key;
|
||||
free(key);
|
||||
rliza_push_object(rliza, value);
|
||||
} else if (**content == '}') {
|
||||
break;
|
||||
} else {
|
||||
// Parse error
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
if ((**content != '}')) {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
(*content)++;
|
||||
return rliza;
|
||||
} else if (**content == '[') {
|
||||
rliza->type = RLIZA_ARRAY;
|
||||
(*content)++;
|
||||
char *result;
|
||||
static char *seek_for4[] = {"[", "{", "\"", "d", ",", "]", "null", "true", "false", NULL};
|
||||
while ((result = (char *)rliza_seek_string(content, seek_for4)) != NULL && *result) {
|
||||
if (**content == ',') {
|
||||
(*content)++;
|
||||
|
||||
} else if (**content == ']') {
|
||||
break;
|
||||
}
|
||||
rliza_t *obj = _rliza_loads(content);
|
||||
if (!obj) {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
rliza_push(rliza, obj);
|
||||
if (!**content) {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (**content != ']') {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
(*content)++;
|
||||
return rliza;
|
||||
} else if (**content >= '0' && **content <= '9') {
|
||||
char *ptr = *content;
|
||||
bool is_decimal = false;
|
||||
|
||||
while (**content) {
|
||||
if (**content == '.') {
|
||||
is_decimal = true;
|
||||
} else if (!isdigit(**content)) {
|
||||
break;
|
||||
}
|
||||
(*content)++;
|
||||
}
|
||||
if (*(*content - 1) == '.') {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
if (!**content) {
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
if (is_decimal) {
|
||||
rliza->type = RLIZA_NUMBER;
|
||||
rliza->content.number = strtod(ptr, NULL);
|
||||
} else {
|
||||
rliza->type = RLIZA_INTEGER;
|
||||
rliza->content.integer = strtoll(ptr, NULL, 10);
|
||||
}
|
||||
return rliza;
|
||||
} else if (!strncmp(*content, "true", 4)) {
|
||||
rliza->type = RLIZA_BOOLEAN;
|
||||
rliza->content.boolean = true;
|
||||
*content += 4;
|
||||
|
||||
return rliza;
|
||||
} else if (!strncmp(*content, "false", 5)) {
|
||||
rliza->type = RLIZA_BOOLEAN;
|
||||
rliza->content.boolean = false;
|
||||
*content += 5;
|
||||
|
||||
return rliza;
|
||||
} else if (!strncmp(*content, "null", 4)) {
|
||||
rliza->type = RLIZA_NULL;
|
||||
*content += 4;
|
||||
|
||||
return rliza;
|
||||
}
|
||||
// Parsing error
|
||||
rliza_free(rliza);
|
||||
return NULL;
|
||||
}
|
||||
rliza_t *rliza_loads(char **content) {
|
||||
if (!content || !**content) {
|
||||
return NULL;
|
||||
}
|
||||
char *original_content = *content;
|
||||
rliza_t *result = _rliza_loads(content);
|
||||
if (!result) {
|
||||
*content = original_content;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char *rliza_dumps(rliza_t *rliza) {
|
||||
size_t size = 4096;
|
||||
char *content = (char *)calloc(size, sizeof(char));
|
||||
content[0] = 0;
|
||||
if (rliza->type == RLIZA_INTEGER) {
|
||||
if (rliza->key) {
|
||||
sprintf(content, "\"%s\":%lld", rliza->key, rliza->content.integer);
|
||||
} else {
|
||||
sprintf(content, "%lld", rliza->content.integer);
|
||||
}
|
||||
} else if (rliza->type == RLIZA_STRING) {
|
||||
|
||||
// char *escaped_string = (char *)calloc(strlen((char *)rliza->content.string) * 2 + 1024,sizeof(char));
|
||||
char *escaped_string = rliza->content.string;
|
||||
// rstrstripslashes((char *)rliza->content.string, escaped_string);
|
||||
size_t min_size = strlen((char *)escaped_string) + (rliza->key ? strlen(rliza->key) : 0) + 1024;
|
||||
if (size < min_size) {
|
||||
size = min_size + 1;
|
||||
content = realloc(content, size);
|
||||
}
|
||||
if (rliza->key) {
|
||||
char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 20);
|
||||
rstrstripslashes((char *)rliza->key, escaped_key);
|
||||
if (strlen(content) > size) {
|
||||
size = size + strlen(escaped_string) + 20;
|
||||
content = realloc(content, size);
|
||||
}
|
||||
sprintf(content, "\"%s\":\"%s\"", escaped_key, escaped_string);
|
||||
free(escaped_key);
|
||||
} else {
|
||||
size = size + strlen(escaped_string) + 20;
|
||||
content = realloc(content, size);
|
||||
sprintf(content, "\"%s\"", escaped_string);
|
||||
}
|
||||
// free(escaped_string);
|
||||
} else if (rliza->type == RLIZA_NUMBER) {
|
||||
if (rliza->key) {
|
||||
sprintf(content, "\"%s\":%f", rliza->key, rliza->content.number);
|
||||
} else {
|
||||
sprintf(content, "%f", rliza->content.number);
|
||||
}
|
||||
int last_zero = 0;
|
||||
bool beyond_dot = false;
|
||||
for (size_t i = 0; i < strlen(content); i++) {
|
||||
if (content[i] == '.') {
|
||||
beyond_dot = true;
|
||||
} else if (beyond_dot == true) {
|
||||
if (content[i - 1] != '.') {
|
||||
if (content[i] == '0') {
|
||||
if (!last_zero)
|
||||
last_zero = i;
|
||||
} else {
|
||||
last_zero = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_zero != 0) {
|
||||
content[last_zero] = 0;
|
||||
}
|
||||
} else if (rliza->type == RLIZA_BOOLEAN) {
|
||||
if (rliza->key) {
|
||||
sprintf(content, "\"%s\":%s", rliza->key, rliza->content.boolean ? "true" : "false");
|
||||
} else {
|
||||
sprintf(content, "%s", rliza->content.boolean ? "true" : "false");
|
||||
}
|
||||
} else if (rliza->type == RLIZA_OBJECT) {
|
||||
|
||||
strcat(content, "{");
|
||||
if (rliza->key) {
|
||||
strcat(content, "\"");
|
||||
strcat(content, rliza->key);
|
||||
strcat(content, "\":{");
|
||||
}
|
||||
// bool add_braces = false;
|
||||
for (unsigned i = 0; i < rliza->count; i++) {
|
||||
char *content_chunk = rliza_dumps(rliza->content.map[i]);
|
||||
char *content_chunk_stripped = content_chunk;
|
||||
if (*content_chunk_stripped == '{') {
|
||||
content_chunk_stripped++;
|
||||
content_chunk_stripped[strlen(content_chunk_stripped) - 1] = 0;
|
||||
}
|
||||
if (strlen(content_chunk_stripped) + strlen(content) > size) {
|
||||
size += strlen(content_chunk_stripped) + 20;
|
||||
content = realloc(content, size);
|
||||
}
|
||||
strcat(content, content_chunk_stripped);
|
||||
free(content_chunk);
|
||||
|
||||
strcat(content, ",");
|
||||
}
|
||||
if (content[strlen(content) - 1] == ',') {
|
||||
content[strlen(content) - 1] = '\0';
|
||||
|
||||
if (rliza->key) {
|
||||
strcat(content, "}");
|
||||
}
|
||||
}
|
||||
strcat(content, "}");
|
||||
} else if (rliza->type == RLIZA_ARRAY) {
|
||||
if (rliza->key) {
|
||||
char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 1);
|
||||
rstraddslashes((char *)rliza->key, escaped_key);
|
||||
if (strlen(escaped_key) > size) {
|
||||
size = strlen(escaped_key) + 10;
|
||||
content = realloc(content, size);
|
||||
}
|
||||
sprintf(content, "\"%s\":[", escaped_key);
|
||||
free(escaped_key);
|
||||
} else
|
||||
strcpy(content, "[");
|
||||
for (unsigned i = 0; i < rliza->count; i++) {
|
||||
char *content_chunk = rliza_dumps(rliza->content.map[i]);
|
||||
char *content_chunk_stripped = content_chunk;
|
||||
if (*content_chunk_stripped == '{') {
|
||||
// content_chunk_stripped++;
|
||||
// content_chunk_stripped[strlen(content_chunk_stripped) - 1] = 0;
|
||||
}
|
||||
if (strlen(content_chunk_stripped) + strlen(content) > size) {
|
||||
size += strlen(content_chunk_stripped) + 20;
|
||||
content = realloc(content, size);
|
||||
}
|
||||
strcat(content, content_chunk_stripped);
|
||||
free(content_chunk);
|
||||
strcat(content, ",");
|
||||
}
|
||||
if (content[strlen(content) - 1] != '[')
|
||||
content[strlen(content) - 1] = 0;
|
||||
strcat(content, "]");
|
||||
} else if (rliza->type == RLIZA_NULL) {
|
||||
|
||||
if (rliza->key) {
|
||||
char *escaped_key = (char *)malloc(strlen((char *)rliza->key) * 2 + 1);
|
||||
rstraddslashes((char *)rliza->key, escaped_key);
|
||||
sprintf(content, "\"%s\":null", escaped_key);
|
||||
free(escaped_key);
|
||||
} else
|
||||
strcpy(content, "null");
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
void rliza_dumpss(rliza_t *rliza) {
|
||||
char *output = rliza_dumps(rliza);
|
||||
printf("%s\n", output);
|
||||
free(output);
|
||||
}
|
||||
|
||||
void rliza_push(rliza_t *self, rliza_t *obj) { rliza_push_object(self, obj); }
|
||||
|
||||
int rliza_validate(char *json_content) {
|
||||
if (!json_content || !*json_content) {
|
||||
return false;
|
||||
}
|
||||
char *json_contentp = json_content;
|
||||
rliza_t *to_object = _rliza_loads(&json_contentp);
|
||||
if (to_object) {
|
||||
rliza_free(to_object);
|
||||
return json_contentp - json_content;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
41
rliza.py
Normal file
41
rliza.py
Normal file
@ -0,0 +1,41 @@
|
||||
import ctypes as ct
|
||||
|
||||
#https://cylab.be/blog/235/calling-c-from-python
|
||||
|
||||
libc = ct.cdll.LoadLibrary("./build/librlib.so")
|
||||
libc.rliza_validate.argtypes = [ct.c_char_p]
|
||||
|
||||
def count(data):
|
||||
count = 0
|
||||
jsonu8 = data.encode('utf-8')
|
||||
while True:
|
||||
result = libc.rliza_validate(jsonu8)
|
||||
if not result:
|
||||
break
|
||||
jsonu8 = jsonu8[result:]
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def length(data):
|
||||
count = 0
|
||||
jsonu8 = data.encode('utf-8')
|
||||
return libc.rliza_validate(jsonu8)
|
||||
|
||||
import unittest
|
||||
|
||||
class RlizaTestCase(unittest.TestCase):
|
||||
def test_count(self):
|
||||
self.assertEqual(count("{}[][]{"), 3)
|
||||
self.assertEqual(count("{}[][]{}"), 4)
|
||||
def test_length(self):
|
||||
self.assertEqual(length("{"),0)
|
||||
self.assertEqual(length("{}"),2)
|
||||
self.assertEqual(length("{}{}"),2)
|
||||
|
||||
def test():
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(RlizaTestCase)
|
||||
runner = unittest.TextTestRunner()
|
||||
runner.run(suite)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
44
rmalloc.c
Normal file
44
rmalloc.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include "rtest.h"
|
||||
|
||||
#include "rmalloc.h"
|
||||
|
||||
void rtest_malloc() {
|
||||
rtest_banner("count");
|
||||
void *x = malloc(10);
|
||||
void *y = malloc(10);
|
||||
void *z = malloc(10);
|
||||
void *ptr = NULL;
|
||||
realloc(ptr, 10);
|
||||
void *w = calloc(1, 10);
|
||||
rtest_true(rmalloc_alloc_count == 5);
|
||||
|
||||
rtest_banner("free") x = free(x);
|
||||
rtest_true(x == NULL);
|
||||
rtest_true(rmalloc_count == 4);
|
||||
|
||||
rtest_banner("another free") y = free(y);
|
||||
rtest_true(y == NULL);
|
||||
rtest_true(rmalloc_count == 3);
|
||||
|
||||
rtest_banner("third free") z = free(z);
|
||||
rtest_true(z == NULL);
|
||||
rtest_true(rmalloc_count == 2);
|
||||
|
||||
rtest_banner("third four") w = free(w);
|
||||
rtest_true(w == NULL);
|
||||
rtest_true(rmalloc_count == 1);
|
||||
|
||||
rtest_banner("third five") ptr = free(ptr);
|
||||
rtest_true(ptr == NULL);
|
||||
rtest_true(rmalloc_count == 0);
|
||||
|
||||
rtest_banner("totals") rtest_true(rmalloc_alloc_count == 5);
|
||||
rtest_true(rmalloc_free_count == 5);
|
||||
rtest_true(rmalloc_count == 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
rtest_banner("malloc.h");
|
||||
rtest_malloc();
|
||||
return rtest_end("rtest_malloc");
|
||||
}
|
145
rmalloc.h
Normal file
145
rmalloc.h
Normal file
@ -0,0 +1,145 @@
|
||||
#ifndef RMALLOC_H
|
||||
#define RMALLOC_H
|
||||
#ifndef RMALLOC_OVERRIDE
|
||||
#define RMALLOC_OVERRIDE 1
|
||||
#endif
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#ifndef _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#endif
|
||||
#ifndef ulonglong
|
||||
#define ulonglong unsigned long long
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
#include "rtemp.h"
|
||||
#ifdef _POSIX_C_SOURCE_TEMP
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
|
||||
#undef _POSIX_C_SOURCE_TEMP
|
||||
#else
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
ulonglong rmalloc_count = 0;
|
||||
ulonglong rmalloc_alloc_count = 0;
|
||||
ulonglong rmalloc_free_count = 0;
|
||||
ulonglong rmalloc_total_bytes_allocated = 0;
|
||||
|
||||
void *_rmalloc_prev_realloc_obj = NULL;
|
||||
size_t _rmalloc_prev_realloc_obj_size = 0;
|
||||
|
||||
void *rmalloc(size_t size) {
|
||||
void *result;
|
||||
while (!(result = malloc(size))) {
|
||||
fprintf(stderr, "Warning: malloc failed, trying again.\n");
|
||||
}
|
||||
rmalloc_count++;
|
||||
rmalloc_alloc_count++;
|
||||
rmalloc_total_bytes_allocated += size;
|
||||
return result;
|
||||
}
|
||||
void *rcalloc(size_t count, size_t size) {
|
||||
void *result;
|
||||
while (!(result = calloc(count, size))) {
|
||||
fprintf(stderr, "Warning: calloc failed, trying again.\n");
|
||||
}
|
||||
rmalloc_alloc_count++;
|
||||
rmalloc_count++;
|
||||
rmalloc_total_bytes_allocated += count * size;
|
||||
return result;
|
||||
}
|
||||
void *rrealloc(void *obj, size_t size) {
|
||||
if (!obj) {
|
||||
rmalloc_count++;
|
||||
}
|
||||
|
||||
rmalloc_alloc_count++;
|
||||
if (obj == _rmalloc_prev_realloc_obj) {
|
||||
rmalloc_total_bytes_allocated += size - _rmalloc_prev_realloc_obj_size;
|
||||
_rmalloc_prev_realloc_obj_size = size - _rmalloc_prev_realloc_obj_size;
|
||||
|
||||
} else {
|
||||
_rmalloc_prev_realloc_obj_size = size;
|
||||
}
|
||||
void *result;
|
||||
while (!(result = realloc(obj, size))) {
|
||||
fprintf(stderr, "Warning: realloc failed, trying again.\n");
|
||||
}
|
||||
_rmalloc_prev_realloc_obj = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char *rstrdup(const char *s) {
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
char *result;
|
||||
size_t size = strlen(s) + 1;
|
||||
|
||||
result = rmalloc(size);
|
||||
memcpy(result, s, size);
|
||||
rmalloc_total_bytes_allocated += size;
|
||||
return result;
|
||||
}
|
||||
void *rfree(void *obj) {
|
||||
rmalloc_count--;
|
||||
rmalloc_free_count++;
|
||||
free(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if RMALLOC_OVERRIDE
|
||||
#define malloc rmalloc
|
||||
#define calloc rcalloc
|
||||
#define realloc rrealloc
|
||||
#define free rfree
|
||||
#define strdup rstrdup
|
||||
#endif
|
||||
|
||||
char *rmalloc_lld_format(ulonglong num) {
|
||||
|
||||
char res[100];
|
||||
res[0] = 0;
|
||||
sprintf(res, "%'lld", num);
|
||||
char *resp = res;
|
||||
while (*resp) {
|
||||
if (*resp == ',')
|
||||
*resp = '.';
|
||||
resp++;
|
||||
}
|
||||
return sbuf(res);
|
||||
}
|
||||
|
||||
char *rmalloc_bytes_format(int factor, ulonglong num) {
|
||||
char *sizes[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
|
||||
if (num > 1024) {
|
||||
return rmalloc_bytes_format(factor + 1, num / 1024);
|
||||
}
|
||||
char res[100];
|
||||
sprintf(res, "%s %s", rmalloc_lld_format(num), sizes[factor]);
|
||||
return sbuf(res);
|
||||
}
|
||||
|
||||
char *rmalloc_stats() {
|
||||
static char res[200];
|
||||
res[0] = 0;
|
||||
// int original_locale = localeconv();
|
||||
setlocale(LC_NUMERIC, "en_US.UTF-8");
|
||||
sprintf(res, "Memory usage: %s, %s (re)allocated, %s unqiue free'd, %s in use.", rmalloc_bytes_format(0, rmalloc_total_bytes_allocated),
|
||||
rmalloc_lld_format(rmalloc_alloc_count), rmalloc_lld_format(rmalloc_free_count),
|
||||
|
||||
rmalloc_lld_format(rmalloc_count));
|
||||
// setlocale(LC_NUMERIC, original_locale);
|
||||
|
||||
setlocale(LC_NUMERIC, "");
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
35
rmath.h
Normal file
35
rmath.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef RMATH_H
|
||||
#define RMATH_H
|
||||
#include <math.h>
|
||||
|
||||
#ifndef ceil
|
||||
double ceil(double x) {
|
||||
if (x == (double)(long long)x) {
|
||||
return x;
|
||||
} else if (x > 0.0) {
|
||||
return (double)(long long)x + 1.0;
|
||||
} else {
|
||||
return (double)(long long)x;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef floor
|
||||
double floor(double x) {
|
||||
if (x >= 0.0) {
|
||||
return (double)(long long)x;
|
||||
} else {
|
||||
double result = (double)(long long)x;
|
||||
return (result == x) ? result : result - 1.0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef modf
|
||||
double modf(double x, double *iptr) {
|
||||
double int_part = (x >= 0.0) ? floor(x) : ceil(x);
|
||||
*iptr = int_part;
|
||||
return x - int_part;
|
||||
}
|
||||
#endif
|
||||
#endif
|
3
rmerge.c
Normal file
3
rmerge.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include "rmerge.h"
|
||||
|
||||
int main(int argc, char *argv[]) { return rmerge_main(argc, argv); }
|
178
rmerge.h
Normal file
178
rmerge.h
Normal file
@ -0,0 +1,178 @@
|
||||
#ifndef RMERGE_H
|
||||
#define RMERGE_H
|
||||
// #include "../mrex/rmatch.h"
|
||||
#include "rlexer.h"
|
||||
#include "rmalloc.h"
|
||||
#include "rprint.h"
|
||||
#include "rrex3.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "remo.h"
|
||||
|
||||
bool has_error = false;
|
||||
|
||||
char *extract_script_src_include(char *line, char *include_path) {
|
||||
include_path[0] = 0;
|
||||
rrex3_t *rrex;
|
||||
rrex = rrex3(NULL, line, "<script.*src=\"(.*)\".*<.*script.*>");
|
||||
if (rrex) {
|
||||
strcpy(include_path, rrex->matches[0]);
|
||||
rrex3_free(rrex);
|
||||
return include_path;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *extract_c_local_include(char *line, char *include_path) {
|
||||
//
|
||||
/*
|
||||
char res;
|
||||
res= rmatch_extract(line, "#include.*"\".*\"");
|
||||
|
||||
|
||||
printf("%MATCH:%s\n", res);
|
||||
*/
|
||||
|
||||
include_path[0] = 0;
|
||||
rrex3_t *rrex;
|
||||
rrex = rrex3(NULL, line, "[^\\\\*]^#include .*\"(.*)\"");
|
||||
if (rrex) {
|
||||
strcpy(include_path, rrex->matches[0]);
|
||||
rrex3_free(rrex);
|
||||
return include_path;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *rmerge_readline(FILE *f) {
|
||||
static char data[4096];
|
||||
data[0] = 0;
|
||||
int index = 0;
|
||||
char c;
|
||||
while ((c = fgetc(f)) != EOF) {
|
||||
if (c != '\0') {
|
||||
data[index] = c;
|
||||
index++;
|
||||
if (c == '\n')
|
||||
break;
|
||||
}
|
||||
}
|
||||
data[index] = 0;
|
||||
if (data[0] == 0)
|
||||
return NULL;
|
||||
return data;
|
||||
}
|
||||
void writestring(FILE *f, char *line) {
|
||||
char c;
|
||||
while ((c = *line) != '\0') {
|
||||
fputc(c, f);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
char files_history[8096];
|
||||
char files_duplicate[8096];
|
||||
bool is_merging = false;
|
||||
|
||||
void merge_file(char *source, FILE *d) {
|
||||
if (is_merging == false) {
|
||||
is_merging = true;
|
||||
files_history[0] = 0;
|
||||
files_duplicate[0] = 0;
|
||||
}
|
||||
if (strstr(files_history, source)) {
|
||||
if (strstr(files_duplicate, source)) {
|
||||
rprintmf(stderr, "\\l Already included: %s. Already on duplicate list.\n", source);
|
||||
} else {
|
||||
rprintcf(stderr, "\\l Already included: %s. Adding to duplicate list.\n", source);
|
||||
strcat(files_duplicate, source);
|
||||
strcat(files_duplicate, "\n");
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
rprintgf(stderr, "\\l Merging: %s.\n", source);
|
||||
strcat(files_history, source);
|
||||
strcat(files_history, "\n");
|
||||
}
|
||||
FILE *fd = fopen(source, "rb");
|
||||
if (!fd) {
|
||||
rprintrf(stderr, "\\l File does not exist: %s\n", source);
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
char *line;
|
||||
char include_path[4096];
|
||||
while ((line = rmerge_readline(fd))) {
|
||||
|
||||
include_path[0] = 0;
|
||||
if (!*line)
|
||||
break;
|
||||
|
||||
//
|
||||
char *inc = extract_c_local_include(line, include_path);
|
||||
if (!inc)
|
||||
inc = extract_script_src_include(line, include_path);
|
||||
|
||||
/*
|
||||
if (!strncmp(line, "#include ", 9)) {
|
||||
int index = 0;
|
||||
while (line[index] != '"' && line[index] != 0) {
|
||||
index++;
|
||||
}
|
||||
if (line[index] == '"') {
|
||||
int pindex = 0;
|
||||
index++;
|
||||
while (line[index] != '"') {
|
||||
include_path[pindex] = line[index];
|
||||
pindex++;
|
||||
index++;
|
||||
}
|
||||
if (line[index] != '"') {
|
||||
include_path[0] = 0;
|
||||
} else {
|
||||
include_path[pindex] = '\0';
|
||||
}
|
||||
}
|
||||
}*/
|
||||
if (inc) {
|
||||
merge_file(inc, d);
|
||||
} else {
|
||||
writestring(d, line);
|
||||
}
|
||||
}
|
||||
fclose(fd);
|
||||
writestring(d, "\n");
|
||||
}
|
||||
|
||||
int rmerge_main(int argc, char *argv[]) {
|
||||
char *file_input = NULL;
|
||||
if (argc != 2) {
|
||||
printf("Usage: <input-file>\n");
|
||||
} else {
|
||||
file_input = argv[1];
|
||||
// file_output = argv[2];
|
||||
}
|
||||
FILE *f = tmpfile();
|
||||
printf("// RETOOR - %s\n", __DATE__);
|
||||
merge_file(file_input, f);
|
||||
rewind(f);
|
||||
char *data;
|
||||
int line_number = 0;
|
||||
while ((data = rmerge_readline(f))) {
|
||||
if (line_number) {
|
||||
printf("/*%.5d*/ ", line_number);
|
||||
line_number++;
|
||||
}
|
||||
printf("%s", data);
|
||||
}
|
||||
printf("\n");
|
||||
if (has_error) {
|
||||
rprintrf(stderr, "\\l Warning: there are errors while merging this file.\n");
|
||||
} else {
|
||||
rprintgf(stderr, "\\l Merge succesful without error(s).%s\n", remo_get("fire"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
42
rnet.c
Normal file
42
rnet.c
Normal file
@ -0,0 +1,42 @@
|
||||
#include "rnet.h"
|
||||
|
||||
void on_client_connect(rnet_socket_t *sock) { printf("%s connected\n", sock->name); }
|
||||
void on_client_read(rnet_socket_t *sock) {
|
||||
unsigned char *data = net_socket_read(sock, 4096);
|
||||
if (!data)
|
||||
return;
|
||||
char *http_headers = "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nConnection: close\r\n\r\n";
|
||||
net_socket_write(sock, (unsigned char *)http_headers, strlen(http_headers));
|
||||
rnet_safe_str((char *)data, sock->bytes_received);
|
||||
// data[11] = 0;
|
||||
printf("%s: %.30s\n", sock->name, data);
|
||||
net_socket_write(sock, data, strlen((char *)data));
|
||||
if (!strncmp((char *)data, "GET ", 4))
|
||||
net_socket_close(sock);
|
||||
}
|
||||
void on_client_close(rnet_socket_t *sock) { printf("%s disconnected\n", sock->name); }
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 2) {
|
||||
printf("usage: [port].\n");
|
||||
return 1;
|
||||
}
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strcmp(argv[i], "test") == 0) {
|
||||
printf("Skipping rnet tests.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
rnet_server_t *server = net_socket_serve((unsigned int)atoi(argv[1]), 10);
|
||||
server->on_connect = on_client_connect;
|
||||
server->on_read = on_client_read;
|
||||
server->on_close = on_client_close;
|
||||
while (true) {
|
||||
if (net_socket_select(server)) {
|
||||
printf("Handled all events.\n");
|
||||
} else {
|
||||
printf("No events to handle.\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
480
rnet.h
Normal file
480
rnet.h
Normal file
@ -0,0 +1,480 @@
|
||||
#ifndef RNET_H
|
||||
#define RNET_H
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#ifndef _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#endif
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#ifdef _POSIX_C_SOURCE_TEMP
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
|
||||
#undef _POSIX_C_SOURCE_TEMP
|
||||
#else
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#define NET_SOCKET_MAX_CONNECTIONS 50000
|
||||
|
||||
typedef struct rnet_socket_t {
|
||||
int fd;
|
||||
char name[50];
|
||||
void *data;
|
||||
size_t bytes_received;
|
||||
size_t bytes_sent;
|
||||
bool connected;
|
||||
void (*on_read)(struct rnet_socket_t *);
|
||||
void (*on_close)(struct rnet_socket_t *);
|
||||
void (*on_connect)(struct rnet_socket_t *);
|
||||
} rnet_socket_t;
|
||||
|
||||
typedef struct rnet_select_result_t {
|
||||
int server_fd;
|
||||
rnet_socket_t **sockets;
|
||||
unsigned int socket_count;
|
||||
} rnet_select_result_t;
|
||||
|
||||
typedef struct rnet_server_t {
|
||||
int socket_fd;
|
||||
rnet_socket_t **sockets;
|
||||
unsigned int socket_count;
|
||||
unsigned int port;
|
||||
unsigned int backlog;
|
||||
rnet_select_result_t *select_result;
|
||||
int max_fd;
|
||||
void (*on_connect)(rnet_socket_t *socket);
|
||||
void (*on_close)(rnet_socket_t *socket);
|
||||
void (*on_read)(rnet_socket_t *socket);
|
||||
} rnet_server_t;
|
||||
|
||||
void rnet_select_result_free(rnet_select_result_t *result);
|
||||
int net_socket_accept(int server_fd);
|
||||
int net_socket_connect(const char *, unsigned int);
|
||||
int net_socket_init();
|
||||
rnet_server_t *net_socket_serve(unsigned int port, unsigned int backlog);
|
||||
rnet_select_result_t *net_socket_select(rnet_server_t *server);
|
||||
rnet_socket_t *net_socket_wait(rnet_socket_t *socket_fd);
|
||||
bool net_set_non_blocking(int sock);
|
||||
bool net_socket_bind(int sock, unsigned int port);
|
||||
bool net_socket_listen(int sock, unsigned int backlog);
|
||||
char *net_socket_name(int sock);
|
||||
size_t net_socket_write(rnet_socket_t *, unsigned char *, size_t);
|
||||
rnet_socket_t *get_net_socket_by_fd(int);
|
||||
unsigned char *net_socket_read(rnet_socket_t *, unsigned int buff_size);
|
||||
void _net_socket_close(int sock);
|
||||
void net_socket_close(rnet_socket_t *sock);
|
||||
|
||||
rnet_server_t *rnet_server_new(int socket_fd, unsigned int port, unsigned int backlog) {
|
||||
rnet_server_t *server = malloc(sizeof(rnet_server_t));
|
||||
server->socket_fd = socket_fd;
|
||||
server->sockets = NULL;
|
||||
server->socket_count = 0;
|
||||
server->port = port;
|
||||
server->backlog = backlog;
|
||||
server->max_fd = -1;
|
||||
server->select_result = NULL;
|
||||
server->on_connect = NULL;
|
||||
server->on_close = NULL;
|
||||
server->on_read = NULL;
|
||||
return server;
|
||||
}
|
||||
|
||||
rnet_server_t *rnet_server_add_socket(rnet_server_t *server, rnet_socket_t *sock) {
|
||||
server->sockets = realloc(server->sockets, sizeof(rnet_socket_t *) * (server->socket_count + 1));
|
||||
server->sockets[server->socket_count] = sock;
|
||||
server->socket_count++;
|
||||
sock->on_read = server->on_read;
|
||||
sock->on_connect = server->on_connect;
|
||||
sock->on_close = server->on_close;
|
||||
sock->connected = true;
|
||||
return server;
|
||||
}
|
||||
|
||||
rnet_socket_t sockets[NET_SOCKET_MAX_CONNECTIONS] = {0};
|
||||
unsigned long sockets_connected = 0;
|
||||
int net_socket_max_fd = 0;
|
||||
unsigned long sockets_total = 0;
|
||||
unsigned long sockets_disconnected = 0;
|
||||
unsigned long sockets_concurrent_record = 0;
|
||||
unsigned long sockets_errors = 0;
|
||||
|
||||
bool net_set_non_blocking(int sock) {
|
||||
int flags = fcntl(sock, F_GETFL, 0);
|
||||
if (flags < 0) {
|
||||
perror("fcntl");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
perror("fcntl");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int net_socket_init() {
|
||||
int socket_fd = -1;
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
int opt = 1;
|
||||
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||
perror("Socket failed.\n");
|
||||
return false;
|
||||
}
|
||||
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
|
||||
perror("Setsockopt failed.\n");
|
||||
close(socket_fd);
|
||||
return false;
|
||||
}
|
||||
net_set_non_blocking(socket_fd);
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
char *net_socket_name(int fd) {
|
||||
rnet_socket_t *rnet_socket = get_net_socket_by_fd(fd);
|
||||
if (rnet_socket) {
|
||||
return rnet_socket->name;
|
||||
;
|
||||
}
|
||||
|
||||
// If socket disconnected or is no client from server
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool net_socket_bind(int socket_fd, unsigned int port) {
|
||||
struct sockaddr_in address;
|
||||
|
||||
address.sin_family = AF_INET; // IPv4
|
||||
address.sin_addr.s_addr = INADDR_ANY; // Bind to any available address
|
||||
address.sin_port = htons(port); // Convert port to network byte order
|
||||
|
||||
if (bind(socket_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
perror("Bind failed");
|
||||
close(socket_fd);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int net_socket_connect(const char *host, unsigned int port) {
|
||||
char port_str[10] = {0};
|
||||
sprintf(port_str, "%d", port);
|
||||
int status;
|
||||
int socket_fd = -1;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res;
|
||||
struct addrinfo *p;
|
||||
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if ((status = getaddrinfo(host, port_str, &hints, &res)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (p = res; p != NULL; p = p->ai_next) {
|
||||
if ((socket_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(socket_fd, p->ai_addr, p->ai_addrlen) == -1) {
|
||||
close(socket_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
bool net_socket_listen(int socket_fd, unsigned int backlog) {
|
||||
if (listen(socket_fd, backlog) < 0) { // '3' is the backlog size
|
||||
perror("Listen failed");
|
||||
close(socket_fd);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
rnet_server_t *net_socket_serve(unsigned int port, unsigned int backlog) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
int socket_fd = net_socket_init();
|
||||
net_socket_bind(socket_fd, port);
|
||||
net_socket_listen(socket_fd, backlog);
|
||||
return rnet_server_new(socket_fd, port, backlog);
|
||||
}
|
||||
|
||||
int net_socket_accept(int net_socket_server_fd) {
|
||||
struct sockaddr_in address;
|
||||
int addrlen = sizeof(address);
|
||||
int new_socket = -1;
|
||||
if ((new_socket = accept(net_socket_server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
|
||||
close(new_socket);
|
||||
return -1;
|
||||
} else {
|
||||
|
||||
return new_socket;
|
||||
}
|
||||
}
|
||||
/*
|
||||
static void net_socket_stats(WrenVM *vm)
|
||||
{
|
||||
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
|
||||
wrenSetSlotString(vm, 1, "sockets_total");
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
|
||||
wrenSetSlotDouble(vm, 1, (double)sockets_total);
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
|
||||
wrenSetSlotString(vm, 1, "sockets_concurrent_record");
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
|
||||
wrenSetSlotDouble(vm, 1, (double)sockets_concurrent_record);
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
|
||||
wrenSetSlotString(vm, 1, "sockets_connected");
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
|
||||
wrenSetSlotDouble(vm, 1, (double)sockets_connected);
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
|
||||
wrenSetSlotString(vm, 1, "sockets_disconnected");
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
|
||||
wrenSetSlotDouble(vm, 1, (double)sockets_disconnected);
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
}*/
|
||||
|
||||
size_t net_socket_write(rnet_socket_t *sock, unsigned char *message, size_t size) {
|
||||
ssize_t sent_total = 0;
|
||||
ssize_t sent = 0;
|
||||
ssize_t to_send = size;
|
||||
while ((sent = send(sock->fd, message, to_send, 0))) {
|
||||
if (sent == -1) {
|
||||
sockets_errors++;
|
||||
net_socket_close(sock);
|
||||
break;
|
||||
}
|
||||
if (sent == 0) {
|
||||
printf("EDGE CASE?\n");
|
||||
exit(1);
|
||||
sockets_errors++;
|
||||
net_socket_close(sock);
|
||||
break;
|
||||
}
|
||||
sent_total += sent;
|
||||
if (sent_total == to_send)
|
||||
break;
|
||||
}
|
||||
return sent_total;
|
||||
}
|
||||
|
||||
unsigned char *net_socket_read(rnet_socket_t *sock, unsigned int buff_size) {
|
||||
if (buff_size > 1024 * 1024 + 1) {
|
||||
perror("Buffer too big. Maximum is 1024*1024.\n");
|
||||
exit(1);
|
||||
}
|
||||
static unsigned char buffer[1024 * 1024];
|
||||
buffer[0] = 0;
|
||||
ssize_t received = recv(sock->fd, buffer, buff_size, 0);
|
||||
if (received <= 0) {
|
||||
buffer[0] = 0;
|
||||
net_socket_close(sock);
|
||||
if (received < 0) {
|
||||
sockets_errors++;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
buffer[received + 1] = 0;
|
||||
sock->bytes_received = received;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rnet_socket_t *net_socket_wait(rnet_socket_t *sock) {
|
||||
if (!sock)
|
||||
return NULL;
|
||||
if (sock->fd == -1)
|
||||
return NULL;
|
||||
fd_set read_fds;
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(sock->fd, &read_fds);
|
||||
|
||||
int max_socket_fd = sock->fd;
|
||||
int activity = select(max_socket_fd + 1, &read_fds, NULL, NULL, NULL);
|
||||
if ((activity < 0) && (errno != EINTR)) {
|
||||
// perror("Select error");
|
||||
net_socket_close(sock);
|
||||
return NULL;
|
||||
}
|
||||
if (FD_ISSET(sock->fd, &read_fds)) {
|
||||
return sock;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rnet_safe_str(char *str, size_t length) {
|
||||
if (!str || !length || !*str)
|
||||
return;
|
||||
for (unsigned int i = 0; i < length; i++) {
|
||||
if (str[i] < 32 || str[i] > 126)
|
||||
if (str[i] != 0)
|
||||
str[i] = '.';
|
||||
}
|
||||
str[length] = 0;
|
||||
}
|
||||
|
||||
rnet_select_result_t *rnet_new_socket_select_result(int socket_fd) {
|
||||
rnet_select_result_t *result = (rnet_select_result_t *)malloc(sizeof(rnet_select_result_t));
|
||||
memset(result, 0, sizeof(rnet_select_result_t));
|
||||
result->server_fd = socket_fd;
|
||||
result->socket_count = 0;
|
||||
result->sockets = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rnet_select_result_add(rnet_select_result_t *result, rnet_socket_t *sock) {
|
||||
result->sockets = realloc(result->sockets, sizeof(rnet_socket_t *) * (result->socket_count + 1));
|
||||
result->sockets[result->socket_count] = sock;
|
||||
result->socket_count++;
|
||||
}
|
||||
void rnet_select_result_free(rnet_select_result_t *result) { free(result); }
|
||||
rnet_select_result_t *net_socket_select(rnet_server_t *server) {
|
||||
fd_set read_fds;
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(server->socket_fd, &read_fds);
|
||||
|
||||
server->max_fd = server->socket_fd;
|
||||
int socket_fd = -1;
|
||||
for (unsigned int i = 0; i < server->socket_count; i++) {
|
||||
socket_fd = server->sockets[i]->fd;
|
||||
if (!server->sockets[i]->connected) {
|
||||
continue;
|
||||
}
|
||||
if (socket_fd > 0) {
|
||||
FD_SET(socket_fd, &read_fds);
|
||||
if (socket_fd > server->max_fd) {
|
||||
server->max_fd = socket_fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
int new_socket = -1;
|
||||
struct sockaddr_in address;
|
||||
int addrlen = sizeof(struct sockaddr_in);
|
||||
int activity = select(server->max_fd + 1, &read_fds, NULL, NULL, NULL);
|
||||
if ((activity < 0) && (errno != EINTR)) {
|
||||
perror("Select error\n");
|
||||
return NULL;
|
||||
}
|
||||
if (FD_ISSET(server->socket_fd, &read_fds)) {
|
||||
if ((new_socket = accept(server->socket_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
|
||||
perror("Accept failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// net_set_non_blocking(new_socket);
|
||||
char name[50] = {0};
|
||||
sprintf(name, "fd:%.4d:ip:%12s:port:%.6d", new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
|
||||
rnet_socket_t *sock_obj = NULL;
|
||||
for (unsigned int i = 0; i < server->socket_count; i++) {
|
||||
if (server->sockets && server->sockets[i]->fd == -1) {
|
||||
sock_obj = server->sockets[i];
|
||||
}
|
||||
}
|
||||
if (!sock_obj) {
|
||||
sock_obj = (rnet_socket_t *)malloc(sizeof(rnet_socket_t));
|
||||
rnet_server_add_socket(server, sock_obj);
|
||||
}
|
||||
sock_obj->fd = new_socket;
|
||||
strcpy(sock_obj->name, name);
|
||||
sockets_connected++;
|
||||
sockets_total++;
|
||||
sockets_concurrent_record = sockets_connected > sockets_concurrent_record ? sockets_connected : sockets_concurrent_record;
|
||||
if (new_socket > net_socket_max_fd) {
|
||||
net_socket_max_fd = new_socket;
|
||||
}
|
||||
sock_obj->connected = true;
|
||||
sock_obj->on_connect(sock_obj);
|
||||
}
|
||||
rnet_select_result_t *result = rnet_new_socket_select_result(server->socket_fd);
|
||||
unsigned int readable_count = 0;
|
||||
for (unsigned int i = 0; i < server->socket_count; i++) {
|
||||
if (server->sockets[i]->fd == -1)
|
||||
continue;
|
||||
if (FD_ISSET(server->sockets[i]->fd, &read_fds)) {
|
||||
rnet_select_result_add(result, server->sockets[i]);
|
||||
readable_count++;
|
||||
if (server->sockets[i]->on_read) {
|
||||
server->sockets[i]->on_read(server->sockets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (server->select_result) {
|
||||
rnet_select_result_free(server->select_result);
|
||||
server->select_result = NULL;
|
||||
}
|
||||
if (readable_count == 0)
|
||||
rnet_select_result_free(result);
|
||||
return readable_count ? result : NULL;
|
||||
}
|
||||
|
||||
rnet_socket_t *get_net_socket_by_fd(int sock) {
|
||||
for (int i = 0; i < net_socket_max_fd; i++) {
|
||||
if (sockets[i].fd == sock) {
|
||||
return &sockets[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _net_socket_close(int sock) {
|
||||
if (sock > 0) {
|
||||
sockets_connected--;
|
||||
sockets_disconnected++;
|
||||
if (sock > 0) {
|
||||
if (close(sock) == -1) {
|
||||
perror("Error closing socket.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void net_socket_close(rnet_socket_t *sock) {
|
||||
sock->connected = false;
|
||||
if (sock->on_close)
|
||||
sock->on_close(sock);
|
||||
_net_socket_close(sock->fd);
|
||||
sock->fd = -1;
|
||||
}
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
47
rprint.c
Normal file
47
rprint.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include "rprint.h"
|
||||
#include "rbench.h"
|
||||
|
||||
void test_putc(void *arg) {
|
||||
char *str = (char *)arg;
|
||||
char c;
|
||||
while ((c = *str++)) {
|
||||
putc('\r', stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void test_putchar(void *arg) {
|
||||
char *str = (char *)arg;
|
||||
char c;
|
||||
while ((c = *str++)) {
|
||||
putchar('\r');
|
||||
}
|
||||
}
|
||||
void test_fwrite(void *arg) {
|
||||
int length;
|
||||
if (rbf->first) {
|
||||
length = strlen((char *)arg);
|
||||
rbf->data = (void *)&length;
|
||||
} else {
|
||||
length = (intptr_t)&rbf->data;
|
||||
}
|
||||
fwrite((char *)arg, 1, length, stdout);
|
||||
}
|
||||
void test_printf(void *arg) { printf("%s", (char *)arg); }
|
||||
void test_rprint(void *arg) { rprint("%s", (char *)arg); }
|
||||
void test_rprintr(void *arg) { rprintr("%s", (char *)arg); }
|
||||
|
||||
int main() {
|
||||
rbench_t *r = rbench_new();
|
||||
r->stdout = false;
|
||||
r->silent = true;
|
||||
long times = 100000;
|
||||
r->add_function(r, "putc", "chrloop", test_putc);
|
||||
r->add_function(r, "putchar", "chrloop", test_putchar);
|
||||
r->add_function(r, "fwrite", "fileio", test_fwrite);
|
||||
r->add_function(r, "printf", "default", test_printf);
|
||||
r->add_function(r, "rprint", "custom", test_rprint);
|
||||
r->add_function(r, "rprintr", "color", test_rprintr);
|
||||
r->execute1(r, times, " \r");
|
||||
r->execute1(r, times, "\\c\\T\\l\\L \r");
|
||||
return rtest_end("");
|
||||
}
|
287
rprint.h
Normal file
287
rprint.h
Normal file
@ -0,0 +1,287 @@
|
||||
#ifndef RPRINT_H
|
||||
#define RPRINT_H
|
||||
#include "rtime.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
long rpline_number = 0;
|
||||
nsecs_t rprtime = 0;
|
||||
|
||||
int8_t _env_rdisable_colors = -1;
|
||||
bool _rprint_enable_colors = true;
|
||||
|
||||
bool rprint_is_color_enabled() {
|
||||
if (_env_rdisable_colors == -1) {
|
||||
_env_rdisable_colors = getenv("RDISABLE_COLORS") != NULL;
|
||||
}
|
||||
if (_env_rdisable_colors) {
|
||||
_rprint_enable_colors = false;
|
||||
}
|
||||
return _rprint_enable_colors;
|
||||
}
|
||||
|
||||
void rprint_disable_colors() { _rprint_enable_colors = false; }
|
||||
void rprint_enable_colors() { _rprint_enable_colors = true; }
|
||||
void rprint_toggle_colors() { _rprint_enable_colors = !_rprint_enable_colors; }
|
||||
|
||||
void rclear() { printf("\033[2J"); }
|
||||
|
||||
void rprintpf(FILE *f, const char *prefix, const char *format, va_list args) {
|
||||
char *pprefix = (char *)prefix;
|
||||
char *pformat = (char *)format;
|
||||
bool reset_color = false;
|
||||
bool press_any_key = false;
|
||||
char new_format[4096];
|
||||
bool enable_color = rprint_is_color_enabled();
|
||||
memset(new_format, 0, 4096);
|
||||
int new_format_length = 0;
|
||||
char temp[1000];
|
||||
memset(temp, 0, 1000);
|
||||
if (enable_color && pprefix[0]) {
|
||||
strcat(new_format, pprefix);
|
||||
new_format_length += strlen(pprefix);
|
||||
reset_color = true;
|
||||
}
|
||||
while (true) {
|
||||
if (pformat[0] == '\\' && pformat[1] == 'i') {
|
||||
strcat(new_format, "\e[3m");
|
||||
new_format_length += strlen("\e[3m");
|
||||
reset_color = true;
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 'u') {
|
||||
strcat(new_format, "\e[4m");
|
||||
new_format_length += strlen("\e[4m");
|
||||
reset_color = true;
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 'b') {
|
||||
strcat(new_format, "\e[1m");
|
||||
new_format_length += strlen("\e[1m");
|
||||
reset_color = true;
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 'C') {
|
||||
press_any_key = true;
|
||||
rpline_number++;
|
||||
pformat++;
|
||||
pformat++;
|
||||
reset_color = false;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 'k') {
|
||||
press_any_key = true;
|
||||
rpline_number++;
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 'c') {
|
||||
rpline_number++;
|
||||
strcat(new_format, "\e[2J\e[H");
|
||||
new_format_length += strlen("\e[2J\e[H");
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 'L') {
|
||||
rpline_number++;
|
||||
temp[0] = 0;
|
||||
sprintf(temp, "%ld", rpline_number);
|
||||
strcat(new_format, temp);
|
||||
new_format_length += strlen(temp);
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 'l') {
|
||||
rpline_number++;
|
||||
temp[0] = 0;
|
||||
sprintf(temp, "%.5ld", rpline_number);
|
||||
strcat(new_format, temp);
|
||||
new_format_length += strlen(temp);
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 'T') {
|
||||
nsecs_t nsecs_now = nsecs();
|
||||
nsecs_t end = rprtime ? nsecs_now - rprtime : 0;
|
||||
temp[0] = 0;
|
||||
sprintf(temp, "%s", format_time(end));
|
||||
strcat(new_format, temp);
|
||||
new_format_length += strlen(temp);
|
||||
rprtime = nsecs_now;
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else if (pformat[0] == '\\' && pformat[1] == 't') {
|
||||
rprtime = nsecs();
|
||||
pformat++;
|
||||
pformat++;
|
||||
} else {
|
||||
new_format[new_format_length] = *pformat;
|
||||
new_format_length++;
|
||||
if (!*pformat)
|
||||
break;
|
||||
|
||||
// printf("%c",*pformat);
|
||||
pformat++;
|
||||
}
|
||||
}
|
||||
if (reset_color) {
|
||||
strcat(new_format, "\e[0m");
|
||||
new_format_length += strlen("\e[0m");
|
||||
}
|
||||
|
||||
new_format[new_format_length] = 0;
|
||||
vfprintf(f, new_format, args);
|
||||
|
||||
fflush(stdout);
|
||||
if (press_any_key) {
|
||||
nsecs_t s = nsecs();
|
||||
fgetc(stdin);
|
||||
rprtime += nsecs() - s;
|
||||
}
|
||||
}
|
||||
|
||||
void rprintp(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void rprintf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprint(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
#define printf rprint
|
||||
|
||||
// Print line
|
||||
void rprintlf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\\l", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprintl(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\\l", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Black
|
||||
void rprintkf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\e[30m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprintk(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\e[30m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Red
|
||||
void rprintrf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\e[31m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprintr(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\e[31m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Green
|
||||
void rprintgf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\e[32m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprintg(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\e[32m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Yellow
|
||||
void rprintyf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\e[33m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprinty(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\e[33m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Blue
|
||||
void rprintbf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\e[34m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void rprintb(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\e[34m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Magenta
|
||||
void rprintmf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\e[35m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprintm(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\e[35m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Cyan
|
||||
void rprintcf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\e[36m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprintc(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\e[36m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// White
|
||||
void rprintwf(FILE *f, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(f, "\e[37m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void rprintw(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
rprintpf(stdout, "\e[37m", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
#endif
|
10
rrex3.c
Normal file
10
rrex3.c
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
#include "rrex3.h"
|
||||
#include "rtest.h"
|
||||
|
||||
int main() {
|
||||
printf("Testing rrex3 regular expression parser.");
|
||||
rrex3_test();
|
||||
return 0;
|
||||
// return rtest_end("");
|
||||
}
|
158
rrex4.c
Normal file
158
rrex4.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include "rrex4.h"
|
||||
#include "rtest.h"
|
||||
#include "rbench.h"
|
||||
#include <regex.h>
|
||||
|
||||
bool bench_r4(unsigned int times, char *str, char *expr) {
|
||||
RBENCH(times, {
|
||||
r4_t *r = r4(str, expr);
|
||||
|
||||
if (r->valid == false) {
|
||||
|
||||
printf("Bench r4 error\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
r4_free(r);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void bench_c(unsigned int times, char *str, char *expr) {
|
||||
regex_t regex;
|
||||
if (regcomp(®ex, expr, REG_EXTENDED)) {
|
||||
printf("Creg: error in regular expression.\n");
|
||||
exit(1);
|
||||
}
|
||||
RBENCH(times, {
|
||||
if (regexec(®ex, str, 0, NULL, 0)) {
|
||||
printf("Creg: error executing regular expression.\n");
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
regfree(®ex);
|
||||
}
|
||||
|
||||
bool bench(unsigned int times, char *str, char *expr) {
|
||||
printf("%d:(%s)<%s>\n", times, str, expr);
|
||||
printf("c:");
|
||||
bench_c(times, str, expr);
|
||||
printf("r:");
|
||||
bench_r4(times, str, expr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void test_r4_next() {
|
||||
r4_t *r = r4_new();
|
||||
char *str = "abcdefghijklmnop";
|
||||
char *reg = "(\\w\\w\\w\\w)";
|
||||
r = r4(str, reg);
|
||||
assert(r->valid);
|
||||
assert(r->match_count == 1);
|
||||
assert(!strcmp(r->matches[0], "abcd"));
|
||||
// Again with same regex as parameter
|
||||
r = r4_next(r, reg);
|
||||
assert(r->valid);
|
||||
assert(r->match_count == 1);
|
||||
assert(!strcmp(r->matches[0], "efgh"));
|
||||
// Again with same regex as parameter
|
||||
r = r4_next(r, reg);
|
||||
assert(r->valid);
|
||||
assert(r->match_count == 1);
|
||||
assert(!strcmp(r->matches[0], "ijkl"));
|
||||
// Reuse expression, NULL parameter
|
||||
r = r4_next(r, NULL);
|
||||
assert(r->valid);
|
||||
assert(r->match_count == 1);
|
||||
assert(!strcmp(r->matches[0], "mnop"));
|
||||
// No results using r4_next
|
||||
r = r4_next(r, NULL);
|
||||
assert(r->valid);
|
||||
assert(r->match_count == 0);
|
||||
// Again no results using r4_next, Shouldn't crash
|
||||
r = r4_next(r, NULL);
|
||||
assert(r->valid);
|
||||
assert(r->match_count == 0);
|
||||
r4_free(r);
|
||||
}
|
||||
|
||||
void bench_all(unsigned int times) {
|
||||
assert(bench(times, "suvw",
|
||||
"[abcdefghijklmnopqrstuvw][abcdefghijklmnopqrstuvw]["
|
||||
"abcdefghijklmnopqrstuvw][abcdefghijklmnopqrstuvw]"));
|
||||
assert(bench(times, "ponyyy", "^p+o.*yyy$$$$"));
|
||||
assert(bench(times, " ponyyzd", "p+o.*yyzd$$$$"));
|
||||
assert(bench(times, "abc", "def|gek|abc"));
|
||||
assert(bench(times, "abc", "def|a?b?c|def"));
|
||||
assert(bench(times, "NL18RABO0322309700", "([A-Z]{2})([0-9]{2})([A-Z]{4}[0-9])([0-9]+)$"));
|
||||
}
|
||||
|
||||
bool r4_match_stats(char *str, char *expr) {
|
||||
r4_t *r = r4(str, expr);
|
||||
bool result = r->valid;
|
||||
printf("%d:(%s)<%s>\n", r->validation_count, r->_str, r->_expr);
|
||||
for (unsigned i = 0; i < r->match_count; i++) {
|
||||
printf(" - match: \"%s\"\n", r->matches[i]);
|
||||
}
|
||||
r4_free(r);
|
||||
return result;
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
assert(r4_match_stats("NL18RABO0322309700", "(\\w{2})(\\d{2})(\\w{4}\\d)(\\d{10})"));
|
||||
|
||||
unsigned int times = 1000;
|
||||
bench_all(times);
|
||||
|
||||
RBENCH(1, {
|
||||
assert(r4_match_stats("#define DEFINETEST 1\n", "#define\\s+(\\w[\\d\\w_]+)\\s+([\\w\\d_]+)"));
|
||||
assert(r4_match_stats("#define DEFINETEST 1\n", "#define\\s+(\\w[\\d\\w_]+)\\s+([\\w\\d_]+)"));
|
||||
assert(r4_match_stats("ponyyy", "^p+o.*yyy$$$$"));
|
||||
assert(!r4_match_stats("ponyyy", "p%+o.*yyy$$$$"));
|
||||
assert(!r4_match_stats("ponyyyd", "^p+o.*yyz$$$$"));
|
||||
assert(r4_match_stats("123", "[0-2][2-2][1-3]$"));
|
||||
assert(r4_match_stats("aaaabC5", "(a)(\\w)a*(a)\\w[A-Z][0-9]$"));
|
||||
assert(r4_match_stats("abcdeeeeee", "ab(cdeee)e"));
|
||||
assert(r4_match_stats("1234567", "12(.*)67$"));
|
||||
assert(r4_match_stats("12111678993", "12(.*)67(.*)3$"));
|
||||
assert(r4_match_stats("NL17RABO0322309700", "NL(.*)R(.*)0(.*)0(.*)0$"));
|
||||
|
||||
assert(r4_match_stats("NL18RABO0322309700", "(\\w{2})(\\d{2})(\\w{4}\\d)(\\d+)$"));
|
||||
assert(r4_match_stats("NL18RABO0322309700garbage", "(\\w{2})(\\d{2})(\\w{4}\\d)(\\d+)"));
|
||||
assert(r4_match_stats("NL18RABO0322309700", "(\\w{2})(\\d{2})(\\w{4}\\d)(\\d+)$"));
|
||||
assert(r4_match_stats(" NL18RABO0322309700", "(\\w{2})(\\d{2})(\\w{4}\\d)(\\d+)$"));
|
||||
assert(r4_match_stats(" NL18RABO0322309700", "(\\w{2})(\\d{2})(\\w{4}\\d)(\\d+)$"));
|
||||
assert(r4_match_stats("NL18RABO0", "(\\w\\w)(\\d\\d)(\\w\\w\\w\\w\\d)$"));
|
||||
assert(r4_match_stats("q", "\\q$"));
|
||||
assert(r4_match_stats("ab123", "[a-z0-9]+$"));
|
||||
assert(r4_match_stats("ppppony", "p*pppony"));
|
||||
assert(r4_match_stats("aa", "a{2}$"));
|
||||
assert(r4_match_stats("A23", "[0-2A-z][2-2][1-3]$"));
|
||||
assert(r4_match_stats("z23", "[0-2A-z][2-2][1-3]$"));
|
||||
assert(r4_match_stats("r23", "[0-2Ar][2-2][1-3]$"));
|
||||
assert(r4_match_stats("test", "\\w\\w\\w\\w$"));
|
||||
assert(!r4_match_stats("test", "\\W\\w\\w\\w$"));
|
||||
assert(r4_match_stats("1est", "\\W\\w\\w\\w$"));
|
||||
assert(r4_match_stats("1est", "\\d\\w\\w\\w$"));
|
||||
assert(r4_match_stats("Aest", "\\D\\w\\w\\w$"));
|
||||
assert(r4_match_stats("abc", "[ab]+"));
|
||||
assert(!r4_match_stats("abc", "[ab]+$"));
|
||||
assert(r4_match_stats("abc", "[abc]+$"));
|
||||
assert(!r4_match_stats("a", "[^ba]"));
|
||||
assert(!r4_match_stats("a", "[^ab]"));
|
||||
assert(r4_match_stats(" ponyyzd", "p+o.*yyzd$$$$"));
|
||||
assert(r4_match_stats("abc", "def|gek|abc"));
|
||||
assert(!r4_match_stats("abc", "def|gek|abd"));
|
||||
assert(r4_match_stats("abc", "def|abc|def"));
|
||||
assert(r4_match_stats("suwv", "[abcdesfghijklmnopqrtuvw][abcdefghijklmnopqrstuvw]["
|
||||
"abcdefghijklmnopqrstuvw][abcdefghijklmnopqrstuvw]"));
|
||||
test_r4_next();
|
||||
r4_enable_debug();
|
||||
|
||||
assert(r4_match_stats("123", "(.*)(.*)(.*)"));
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
758
rrex4.h
Normal file
758
rrex4.h
Normal file
@ -0,0 +1,758 @@
|
||||
#ifndef RREX4_H
|
||||
#define RREX4_H
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define R4_DEBUG_a
|
||||
|
||||
#ifdef R4_DEBUG
|
||||
static int _r4_debug = 1;
|
||||
#else
|
||||
static int _r4_debug = 0;
|
||||
#endif
|
||||
|
||||
static char *_format_function_name(const char *name) {
|
||||
static char result[100];
|
||||
result[0] = 0;
|
||||
|
||||
char *new_name = (char *)name;
|
||||
new_name += 11;
|
||||
if (new_name[0] == '_')
|
||||
new_name += 1;
|
||||
if (strlen(new_name) == 0) {
|
||||
return " -";
|
||||
}
|
||||
strcpy(result, new_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
#define DEBUG_VALIDATE_FUNCTION \
|
||||
if (_r4_debug || r4->debug) \
|
||||
printf("DEBUG: %s %s <%s> \"%s\"\n", _format_function_name(__func__), r4->valid ? "valid" : "INVALID", r4->expr, r4->str);
|
||||
|
||||
struct r4_t;
|
||||
|
||||
void r4_enable_debug() { _r4_debug = true; }
|
||||
void r4_disable_debug() { _r4_debug = false; }
|
||||
|
||||
typedef bool (*r4_function)(struct r4_t *);
|
||||
|
||||
typedef struct r4_t {
|
||||
bool debug;
|
||||
bool valid;
|
||||
bool in_block;
|
||||
bool is_greedy;
|
||||
bool in_range;
|
||||
unsigned int backtracking;
|
||||
unsigned int loop_count;
|
||||
unsigned int in_group;
|
||||
unsigned int match_count;
|
||||
unsigned int validation_count;
|
||||
unsigned int start;
|
||||
unsigned int end;
|
||||
unsigned int length;
|
||||
bool (*functions[254])(struct r4_t *);
|
||||
bool (*slash_functions[254])(struct r4_t *);
|
||||
char *_str;
|
||||
char *_expr;
|
||||
char *match;
|
||||
char *str;
|
||||
char *expr;
|
||||
char *str_previous;
|
||||
char *expr_previous;
|
||||
char **matches;
|
||||
} r4_t;
|
||||
|
||||
static bool v4_initiated = false;
|
||||
typedef bool (*v4_function_map)(r4_t *);
|
||||
v4_function_map v4_function_map_global[256];
|
||||
v4_function_map v4_function_map_slash[256];
|
||||
v4_function_map v4_function_map_block[256];
|
||||
|
||||
void r4_free_matches(r4_t *r) {
|
||||
if (!r)
|
||||
return;
|
||||
if (r->match) {
|
||||
free(r->match);
|
||||
r->match = NULL;
|
||||
}
|
||||
if (!r->match_count) {
|
||||
return;
|
||||
}
|
||||
for (unsigned i = 0; i < r->match_count; i++) {
|
||||
free(r->matches[i]);
|
||||
}
|
||||
free(r->matches);
|
||||
r->match_count = 0;
|
||||
r->matches = NULL;
|
||||
}
|
||||
|
||||
void r4_free(r4_t *r) {
|
||||
if (!r)
|
||||
return;
|
||||
r4_free_matches(r);
|
||||
free(r);
|
||||
}
|
||||
|
||||
static bool r4_backtrack(r4_t *r4);
|
||||
static bool r4_validate(r4_t *r4);
|
||||
static void r4_match_add(r4_t *r4, char *extracted);
|
||||
|
||||
static bool r4_validate_literal(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
if (!r4->valid)
|
||||
return false;
|
||||
if (*r4->str != *r4->expr) {
|
||||
r4->valid = false;
|
||||
} else {
|
||||
r4->str++;
|
||||
}
|
||||
r4->expr++;
|
||||
if (r4->in_block || r4->in_range || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
static bool r4_validate_question_mark(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->valid = true;
|
||||
r4->expr++;
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_plus(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->expr++;
|
||||
if (r4->valid == false) {
|
||||
return r4_validate(r4);
|
||||
}
|
||||
char *expr_left = r4->expr_previous;
|
||||
char *expr_right = r4->expr;
|
||||
char *str = r4->str;
|
||||
char *return_expr = NULL;
|
||||
if (*expr_right == ')') {
|
||||
return_expr = expr_right;
|
||||
expr_right++;
|
||||
}
|
||||
r4->is_greedy = false;
|
||||
r4->expr = expr_left;
|
||||
while (r4->valid) {
|
||||
if (*expr_right) {
|
||||
r4->expr = expr_right;
|
||||
r4->is_greedy = true;
|
||||
if (r4_backtrack(r4)) {
|
||||
|
||||
if (return_expr) {
|
||||
r4->str = str;
|
||||
r4->expr = return_expr;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
} else {
|
||||
r4->is_greedy = false;
|
||||
}
|
||||
}
|
||||
r4->valid = true;
|
||||
r4->expr = expr_left;
|
||||
r4->str = str;
|
||||
r4_validate(r4);
|
||||
str = r4->str;
|
||||
}
|
||||
r4->is_greedy = true;
|
||||
r4->valid = true;
|
||||
r4->expr = return_expr ? return_expr : expr_right;
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_dollar(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->expr++;
|
||||
r4->valid = *r4->str == 0;
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_roof(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
if (r4->str != r4->_str) {
|
||||
return false;
|
||||
}
|
||||
r4->expr++;
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_dot(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
if (*r4->str == 0) {
|
||||
return false;
|
||||
}
|
||||
r4->expr++;
|
||||
r4->valid = *r4->str != '\n';
|
||||
r4->str++;
|
||||
|
||||
if (r4->in_block || r4->in_range || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_asterisk(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->expr++;
|
||||
if (r4->valid == false) {
|
||||
r4->valid = true;
|
||||
return r4->valid;
|
||||
// return r4_validate(r4);
|
||||
}
|
||||
char *expr_left = r4->expr_previous;
|
||||
char *expr_right = r4->expr;
|
||||
char *str = r4->str;
|
||||
char *return_expr = NULL;
|
||||
if (*expr_right == ')') {
|
||||
return_expr = expr_right;
|
||||
expr_right++;
|
||||
}
|
||||
r4->is_greedy = false;
|
||||
r4->expr = expr_left;
|
||||
while (r4->valid) {
|
||||
if (*expr_right) {
|
||||
r4->expr = expr_right;
|
||||
r4->is_greedy = true;
|
||||
if (r4_backtrack(r4)) {
|
||||
|
||||
if (return_expr) {
|
||||
r4->str = str;
|
||||
r4->expr = return_expr;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
} else {
|
||||
r4->is_greedy = false;
|
||||
}
|
||||
}
|
||||
r4->valid = true;
|
||||
r4->expr = expr_left;
|
||||
r4->str = str;
|
||||
r4_validate(r4);
|
||||
str = r4->str;
|
||||
}
|
||||
r4->is_greedy = true;
|
||||
r4->valid = true;
|
||||
r4->expr = return_expr ? return_expr : expr_right;
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_pipe(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->expr++;
|
||||
if (r4->valid == true) {
|
||||
return true;
|
||||
} else {
|
||||
r4->valid = true;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_digit(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
if (!isdigit(*r4->str)) {
|
||||
r4->valid = false;
|
||||
} else {
|
||||
r4->str++;
|
||||
}
|
||||
r4->expr++;
|
||||
if (r4->in_block || r4->in_range || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
static bool r4_validate_not_digit(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
if (isdigit(*r4->str)) {
|
||||
r4->valid = false;
|
||||
} else {
|
||||
r4->str++;
|
||||
}
|
||||
r4->expr++;
|
||||
|
||||
if (r4->in_block || r4->in_range || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
static bool r4_validate_word(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
if (!isalpha(*r4->str)) {
|
||||
r4->valid = false;
|
||||
} else {
|
||||
r4->str++;
|
||||
}
|
||||
r4->expr++;
|
||||
|
||||
if (r4->in_block || r4->in_range || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
static bool r4_validate_not_word(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
if (isalpha(*r4->str)) {
|
||||
r4->valid = false;
|
||||
} else {
|
||||
r4->str++;
|
||||
}
|
||||
r4->expr++;
|
||||
|
||||
if (r4->in_block || r4->in_range || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_isrange(char *s) {
|
||||
if (!isalnum(*s)) {
|
||||
return false;
|
||||
}
|
||||
if (*(s + 1) != '-') {
|
||||
return false;
|
||||
}
|
||||
return isalnum(*(s + 2));
|
||||
}
|
||||
|
||||
static bool r4_validate_block_open(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
if (r4->valid == false) {
|
||||
return false;
|
||||
}
|
||||
char *expr_self = r4->expr;
|
||||
r4->expr++;
|
||||
bool reversed = *r4->expr == '^';
|
||||
if (reversed) {
|
||||
r4->expr++;
|
||||
}
|
||||
|
||||
bool valid_once = false;
|
||||
r4->in_block = true;
|
||||
while (*r4->expr != ']') {
|
||||
r4->valid = true;
|
||||
if (r4_isrange(r4->expr)) {
|
||||
char s = *r4->expr;
|
||||
char e = *(r4->expr + 2);
|
||||
r4->expr += 2;
|
||||
if (s > e) {
|
||||
char tempc = s;
|
||||
s = e;
|
||||
e = tempc;
|
||||
}
|
||||
if (*r4->str >= s && *r4->str <= e) {
|
||||
if (!reversed) {
|
||||
r4->str++;
|
||||
}
|
||||
valid_once = true;
|
||||
break;
|
||||
} else {
|
||||
r4->expr++;
|
||||
}
|
||||
} else if (r4_validate(r4)) {
|
||||
valid_once = true;
|
||||
if (reversed)
|
||||
r4->str--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
char *expr_end = strchr(r4->expr, ']');
|
||||
|
||||
r4->expr = expr_end ? expr_end : r4->expr;
|
||||
r4->in_block = false;
|
||||
r4->valid = expr_end && (!reversed ? valid_once : !valid_once);
|
||||
r4->expr++;
|
||||
r4->expr_previous = expr_self;
|
||||
|
||||
if (r4->in_range || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_whitespace(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->valid = strchr("\r\t \n", *r4->str) != NULL;
|
||||
r4->expr++;
|
||||
if (r4->valid) {
|
||||
r4->str++;
|
||||
}
|
||||
if (r4->in_range || r4->in_block || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
static bool r4_validate_not_whitespace(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->valid = strchr("\r\t \n", *r4->str) == NULL;
|
||||
r4->expr++;
|
||||
if (r4->valid) {
|
||||
r4->str++;
|
||||
}
|
||||
if (r4->in_range || r4->in_block || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_range(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION;
|
||||
if (r4->valid == false) {
|
||||
r4->expr++;
|
||||
return false;
|
||||
}
|
||||
char *previous = r4->expr_previous;
|
||||
r4->in_range = true;
|
||||
r4->expr++;
|
||||
unsigned int start = 0;
|
||||
while (isdigit(*r4->expr)) {
|
||||
start = 10 * start;
|
||||
start += *r4->expr - '0';
|
||||
r4->expr++;
|
||||
}
|
||||
if (start != 0)
|
||||
start--;
|
||||
|
||||
unsigned int end = 0;
|
||||
bool variable_end_range = false;
|
||||
if (*r4->expr == ',') {
|
||||
r4->expr++;
|
||||
if (!isdigit(*r4->expr)) {
|
||||
variable_end_range = true;
|
||||
}
|
||||
}
|
||||
while (isdigit(*r4->expr)) {
|
||||
end = end * 10;
|
||||
end += *r4->expr - '0';
|
||||
r4->expr++;
|
||||
}
|
||||
r4->expr++;
|
||||
|
||||
bool valid = true;
|
||||
char *expr_right = r4->expr;
|
||||
for (unsigned int i = 0; i < start; i++) {
|
||||
r4->expr = previous;
|
||||
valid = r4_validate(r4);
|
||||
if (!*r4->str)
|
||||
break;
|
||||
if (!valid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
r4->expr = expr_right;
|
||||
r4->in_range = false;
|
||||
if (!r4->valid)
|
||||
return false;
|
||||
return r4_validate(r4);
|
||||
|
||||
for (unsigned int i = start; i < end; i++) {
|
||||
r4->expr = previous;
|
||||
valid = r4_validate(r4);
|
||||
if (!valid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (variable_end_range) {
|
||||
r4->in_range = false;
|
||||
valid = r4_validate(r4);
|
||||
r4->in_range = true;
|
||||
if (valid) {
|
||||
break;
|
||||
}
|
||||
r4->in_range = true;
|
||||
valid = r4_validate(r4);
|
||||
r4->in_range = false;
|
||||
if (!valid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
r4->valid = valid;
|
||||
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_group_close(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
return r4->valid;
|
||||
}
|
||||
|
||||
static bool r4_validate_group_open(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
char *expr_previous = r4->expr_previous;
|
||||
r4->expr++;
|
||||
bool save_match = r4->in_group == 0;
|
||||
r4->in_group++;
|
||||
char *str_extract_start = r4->str;
|
||||
bool valid = r4_validate(r4);
|
||||
|
||||
if (!valid || *r4->expr != ')') {
|
||||
// this is a valid case if not everything between () matches
|
||||
r4->in_group--;
|
||||
if (save_match == false) {
|
||||
r4->valid = true;
|
||||
}
|
||||
|
||||
// Not direct return? Not sure
|
||||
return r4_validate(r4);
|
||||
}
|
||||
// if(save_match){
|
||||
// r4->match_count++;
|
||||
// }
|
||||
if (save_match) {
|
||||
char *str_extract_end = r4->str;
|
||||
unsigned int extracted_length = str_extract_end - str_extract_start;
|
||||
// strlen(str_extract_start) - strlen(str_extract_end);
|
||||
char *str_extracted = (char *)calloc(sizeof(char), extracted_length + 1);
|
||||
strncpy(str_extracted, str_extract_start, extracted_length);
|
||||
r4_match_add(r4, str_extracted);
|
||||
}
|
||||
assert(*r4->expr == ')');
|
||||
r4->expr++;
|
||||
r4->in_group--;
|
||||
r4->expr_previous = expr_previous;
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static bool r4_validate_slash(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
// The handling code for handling slashes is implemented in r4_validate
|
||||
char *expr_previous = r4->expr_previous;
|
||||
r4->expr++;
|
||||
r4_function f = v4_function_map_slash[(int)*r4->expr];
|
||||
r4->expr_previous = expr_previous;
|
||||
return f(r4);
|
||||
}
|
||||
|
||||
static void r4_match_add(r4_t *r4, char *extracted) {
|
||||
r4->matches = (char **)realloc(r4->matches, (r4->match_count + 1) * sizeof(char *));
|
||||
r4->matches[r4->match_count] = extracted;
|
||||
r4->match_count++;
|
||||
}
|
||||
|
||||
static bool r4_validate_word_boundary_start(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->expr++;
|
||||
if (!r4->valid) {
|
||||
return r4->valid;
|
||||
}
|
||||
r4->valid = isalpha(*r4->str) && (r4->str == r4->_str || !isalpha(*(r4->str - 1)));
|
||||
if (r4->in_range || r4->in_block || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
static bool r4_validate_word_boundary_end(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->expr++;
|
||||
if (!r4->valid) {
|
||||
return r4->valid;
|
||||
}
|
||||
r4->valid = isalpha(*r4->str) && (*(r4->str + 1) == 0 || !isalpha(*(r4->str + 1)));
|
||||
if (r4->in_range || r4->in_block || !r4->is_greedy) {
|
||||
return r4->valid;
|
||||
}
|
||||
return r4_validate(r4);
|
||||
}
|
||||
|
||||
static void v4_init_function_maps() {
|
||||
if (v4_initiated)
|
||||
return;
|
||||
v4_initiated = true;
|
||||
for (__uint8_t i = 0; i < 255; i++) {
|
||||
v4_function_map_global[i] = r4_validate_literal;
|
||||
v4_function_map_slash[i] = r4_validate_literal;
|
||||
v4_function_map_block[i] = r4_validate_literal;
|
||||
}
|
||||
v4_function_map_global['*'] = r4_validate_asterisk;
|
||||
v4_function_map_global['?'] = r4_validate_question_mark;
|
||||
v4_function_map_global['+'] = r4_validate_plus;
|
||||
v4_function_map_global['$'] = r4_validate_dollar;
|
||||
v4_function_map_global['^'] = r4_validate_roof;
|
||||
v4_function_map_global['.'] = r4_validate_dot;
|
||||
v4_function_map_global['|'] = r4_validate_pipe;
|
||||
v4_function_map_global['\\'] = r4_validate_slash;
|
||||
v4_function_map_global['['] = r4_validate_block_open;
|
||||
v4_function_map_global['{'] = r4_validate_range;
|
||||
v4_function_map_global['('] = r4_validate_group_open;
|
||||
v4_function_map_global[')'] = r4_validate_group_close;
|
||||
v4_function_map_slash['b'] = r4_validate_word_boundary_start;
|
||||
v4_function_map_slash['B'] = r4_validate_word_boundary_end;
|
||||
v4_function_map_slash['d'] = r4_validate_digit;
|
||||
v4_function_map_slash['w'] = r4_validate_word;
|
||||
v4_function_map_slash['D'] = r4_validate_not_digit;
|
||||
v4_function_map_slash['W'] = r4_validate_not_word;
|
||||
v4_function_map_slash['s'] = r4_validate_whitespace;
|
||||
v4_function_map_slash['S'] = r4_validate_not_whitespace;
|
||||
v4_function_map_block['\\'] = r4_validate_slash;
|
||||
|
||||
v4_function_map_block['{'] = r4_validate_range;
|
||||
}
|
||||
|
||||
void r4_init(r4_t *r4) {
|
||||
v4_init_function_maps();
|
||||
if (r4 == NULL)
|
||||
return;
|
||||
r4->debug = _r4_debug;
|
||||
r4->valid = true;
|
||||
r4->validation_count = 0;
|
||||
r4->match_count = 0;
|
||||
r4->start = 0;
|
||||
r4->end = 0;
|
||||
r4->length = 0;
|
||||
r4->matches = NULL;
|
||||
}
|
||||
|
||||
static bool r4_looks_behind(char c) { return strchr("?*+{", c) != NULL; }
|
||||
|
||||
r4_t *r4_new() {
|
||||
r4_t *r4 = (r4_t *)malloc(sizeof(r4_t));
|
||||
|
||||
r4_init(r4);
|
||||
|
||||
return r4;
|
||||
}
|
||||
|
||||
static bool r4_pipe_next(r4_t *r4) {
|
||||
char *expr = r4->expr;
|
||||
while (*expr) {
|
||||
if (*expr == '|') {
|
||||
r4->expr = expr + 1;
|
||||
r4->valid = true;
|
||||
return true;
|
||||
}
|
||||
expr++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool r4_backtrack(r4_t *r4) {
|
||||
if (_r4_debug)
|
||||
printf("\033[36mDEBUG: backtrack start (%d)\n", r4->backtracking);
|
||||
r4->backtracking++;
|
||||
char *str = r4->str;
|
||||
char *expr = r4->expr;
|
||||
bool result = r4_validate(r4);
|
||||
r4->backtracking--;
|
||||
if (result == false) {
|
||||
r4->expr = expr;
|
||||
r4->str = str;
|
||||
}
|
||||
if (_r4_debug)
|
||||
printf("DEBUG: backtrack end (%d) result: %d %s\n", r4->backtracking, result, r4->backtracking == 0 ? "\033[0m" : "");
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool r4_validate(r4_t *r4) {
|
||||
DEBUG_VALIDATE_FUNCTION
|
||||
r4->validation_count++;
|
||||
char c_val = *r4->expr;
|
||||
if (c_val == 0) {
|
||||
return r4->valid;
|
||||
}
|
||||
if (!r4_looks_behind(c_val)) {
|
||||
r4->expr_previous = r4->expr;
|
||||
} else if (r4->expr == r4->_expr) {
|
||||
// Regex may not start with a look behind ufnction
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!r4->valid && !r4_looks_behind(*r4->expr)) {
|
||||
if (!r4_pipe_next(r4)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
r4_function f;
|
||||
if (r4->in_block) {
|
||||
f = v4_function_map_block[(int)c_val];
|
||||
} else {
|
||||
f = v4_function_map_global[(int)c_val];
|
||||
}
|
||||
|
||||
r4->valid = f(r4);
|
||||
return r4->valid;
|
||||
}
|
||||
|
||||
char *r4_get_match(r4_t *r) {
|
||||
char *match = (char *)malloc(r->length + 1);
|
||||
strncpy(match, r->_str + r->start, r->length);
|
||||
match[r->length] = 0;
|
||||
return match;
|
||||
}
|
||||
|
||||
static bool r4_search(r4_t *r) {
|
||||
bool valid = true;
|
||||
char *str_next = r->str;
|
||||
while (*r->str) {
|
||||
if (!(valid = r4_validate(r))) {
|
||||
// Move next until we find a match
|
||||
if (!r->backtracking) {
|
||||
r->start++;
|
||||
}
|
||||
str_next++;
|
||||
r->str = str_next;
|
||||
r->expr = r->_expr;
|
||||
r->valid = true;
|
||||
} else {
|
||||
/// HIGH DOUBT
|
||||
if (!r->backtracking) {
|
||||
// r->start = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
r->valid = valid;
|
||||
if (r->valid) {
|
||||
r->end = strlen(r->_str) - strlen(r->str);
|
||||
r->length = r->end - r->start;
|
||||
r->match = r4_get_match(r);
|
||||
}
|
||||
return r->valid;
|
||||
}
|
||||
|
||||
r4_t *r4(const char *str, const char *expr) {
|
||||
r4_t *r = r4_new();
|
||||
r->_str = (char *)str;
|
||||
r->_expr = (char *)expr;
|
||||
r->match = NULL;
|
||||
r->str = r->_str;
|
||||
r->expr = r->_expr;
|
||||
r->str_previous = r->_str;
|
||||
r->expr_previous = r->expr;
|
||||
r->in_block = false;
|
||||
r->is_greedy = true;
|
||||
r->in_group = 0;
|
||||
r->loop_count = 0;
|
||||
r->backtracking = 0;
|
||||
r->in_range = false;
|
||||
r4_search(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r4_t *r4_next(r4_t *r, char *expr) {
|
||||
if (expr) {
|
||||
r->_expr = expr;
|
||||
}
|
||||
r->backtracking = 0;
|
||||
r->expr = r->_expr;
|
||||
r->is_greedy = true;
|
||||
r->in_block = false;
|
||||
r->in_range = false;
|
||||
r->in_group = false;
|
||||
r4_free_matches(r);
|
||||
r4_search(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool r4_match(char *str, char *expr) {
|
||||
r4_t *r = r4(str, expr);
|
||||
bool result = r->valid;
|
||||
r4_free(r);
|
||||
return result;
|
||||
}
|
||||
#endif
|
179
rstring.c
Normal file
179
rstring.c
Normal file
@ -0,0 +1,179 @@
|
||||
|
||||
#include "rstring.h"
|
||||
#include "rtest.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void rstring_test_whitespace() {
|
||||
char *str = malloc(30);
|
||||
str[0] = 0;
|
||||
char *str2 = rcat(10, 10);
|
||||
printf("Numbers: %s\n", str2);
|
||||
char *str3 = "Cool";
|
||||
rcat(str, str3);
|
||||
rcat(str, ' ');
|
||||
rcat(str, 13.37);
|
||||
printf("String: %s\n", str);
|
||||
free(str);
|
||||
|
||||
rtest_banner("rstrip_whitespace");
|
||||
char output[1024];
|
||||
// Test 1
|
||||
char *string1 = " Test 1";
|
||||
rstrip_whitespace(string1, output);
|
||||
rassert(strlen(output) == 6);
|
||||
|
||||
char *string2 = " Test 1";
|
||||
rstrip_whitespace(string2, output);
|
||||
rassert(strlen(output) == 6);
|
||||
|
||||
char *string3 = "Test 1";
|
||||
rstrip_whitespace(string3, output);
|
||||
rassert(strlen(output) == 6);
|
||||
|
||||
char *string4 = "";
|
||||
rstrip_whitespace(string4, output);
|
||||
rassert(strlen(output) == 0);
|
||||
}
|
||||
|
||||
void rstring_test_rstrtokline() {
|
||||
rtest_banner("rstrtokline");
|
||||
char lines[1024] = "Line 1\nLine 2\nLine 3\nLine 4\n333.29\n3.2221";
|
||||
char line[1024];
|
||||
size_t offset = 0;
|
||||
// Test 1
|
||||
while ((offset = rstrtokline(lines, line, offset, true)) && *line) {
|
||||
rassert(strlen(line) == 6);
|
||||
}
|
||||
// Test 2
|
||||
offset = 0;
|
||||
int count = 0;
|
||||
while ((offset = rstrtokline(lines, line, offset, false)) && *line) {
|
||||
size_t expected_length = count < 5 ? 7 : 6;
|
||||
count++;
|
||||
rassert(strlen(line) == expected_length);
|
||||
}
|
||||
// Test 3
|
||||
offset = 0;
|
||||
strcat(lines, "\n");
|
||||
count = 0;
|
||||
while ((offset = rstrtokline(lines, line, offset, true)) && *line) {
|
||||
count++;
|
||||
size_t expected_length = 6;
|
||||
rassert(strlen(line) == expected_length);
|
||||
}
|
||||
}
|
||||
|
||||
void sort_test(char *text, char *text_sort_expected) {
|
||||
char sorted_text[4096];
|
||||
rstrsort(text, sorted_text);
|
||||
rassert(!strcmp(text_sort_expected, sorted_text));
|
||||
}
|
||||
|
||||
void rstring_test_rstrsort() {
|
||||
rtest_banner("Sorting string content");
|
||||
sort_test("Line 3\nLine 2\nLine 4\nLine 1\nQQ 333.29\n3.22\n1337.29\n3.22\n",
|
||||
"Line 1\nLine 2\nLine 3\nLine 4\n3.22\n3.22\nQQ 333.29\n1337.29\n");
|
||||
sort_test("333.29\n3.22\n1337.29\n3.22\nLine 3\nThe original line 2\nLine "
|
||||
"4\nLine 1\n",
|
||||
"Line 1\nThe original line 2\nLine 3\nLine "
|
||||
"4\n3.22\n3.22\n333.29\n1337.29\n");
|
||||
}
|
||||
|
||||
void rstring_test_rformat_number() {
|
||||
rtest_banner("Format number to human readable");
|
||||
rassert(!strcmp(rformat_number(100), "100"));
|
||||
rassert(!strcmp(rformat_number(1001), "1.001"));
|
||||
rassert(!strcmp(rformat_number(10001), "10.001"));
|
||||
rassert(!strcmp(rformat_number(100001), "100.001"));
|
||||
rassert(!strcmp(rformat_number(1000001), "1.000.001"));
|
||||
rassert(!strcmp(rformat_number(1000000001), "1.000.000.001"));
|
||||
rassert(!strcmp(rformat_number(1000000000001), "1.000.000.000.001"));
|
||||
rassert(!strcmp(rformat_number(1000000000000001), "1.000.000.000.000.001"));
|
||||
rassert(!strcmp(rformat_number(-1000000000000001), "-1.000.000.000.000.001"));
|
||||
}
|
||||
|
||||
void rstring_test_rstraddslashes() {
|
||||
rtest_banner("Addslashes");
|
||||
char input[] = "\r\t\n\b\f test";
|
||||
char output[100];
|
||||
rstraddslashes(input, output);
|
||||
rassert(!strcmp((char *)output, "\\r\\t\\n\\b\\f test"));
|
||||
}
|
||||
|
||||
void rstring_test_rstrstripslashes() {
|
||||
rtest_banner("Stripslashes");
|
||||
char input[] = "\\r\\t\\n\\b\\f\" test";
|
||||
char output[100];
|
||||
rstrstripslashes(input, output);
|
||||
rassert(!strcmp((char *)output, "\r\t\n\b\f\" test"));
|
||||
}
|
||||
|
||||
void rstring_test_rstrstartswith() {
|
||||
rtest_banner("Starts with");
|
||||
rassert(rstrstartswith("abc", "abc"));
|
||||
rassert(rstrstartswith("abc", "ab"));
|
||||
rassert(rstrstartswith("abc", "a"));
|
||||
rassert(rstrstartswith("", ""));
|
||||
rassert(!rstrstartswith("abc", "abcdef"));
|
||||
rassert(!rstrstartswith("abc", "b"));
|
||||
rassert(!rstrstartswith("abc", "bc"));
|
||||
rassert(!rstrstartswith("abc", "c"));
|
||||
}
|
||||
|
||||
void rstring_test_rstrendswith() {
|
||||
rtest_banner("Ends with");
|
||||
rassert(rstrendswith("abc", "abc"));
|
||||
rassert(rstrendswith("abc", "bc"));
|
||||
rassert(rstrendswith("abc", "c"));
|
||||
rassert(rstrendswith("", ""));
|
||||
rassert(!rstrendswith("abc", "a"));
|
||||
rassert(!rstrendswith("abc", "ab"));
|
||||
rassert(!rstrendswith("abc", "abcdef"));
|
||||
}
|
||||
|
||||
void rstring_test_rstrmove() {
|
||||
rtest_banner("Move str");
|
||||
// Test 1
|
||||
char to_move_1[] = "abc?";
|
||||
rstrmove(to_move_1, 3, 1, 0);
|
||||
rassert(!strcmp(to_move_1, "?abc"));
|
||||
// Test 2
|
||||
char to_move_2[] = "abc?defgabc";
|
||||
rstrmove(to_move_2, 3, 5, 0);
|
||||
rassert(!strcmp(to_move_2, "?defgabcabc"));
|
||||
// Test 3
|
||||
char to_move_3[] = "abc?defg";
|
||||
rstrmove(to_move_3, 0, 3, 7);
|
||||
rassert(!strcmp(to_move_3, "?defgabc"));
|
||||
|
||||
// Test 4
|
||||
char to_move_4[] = "abc?defgaa";
|
||||
rstrmove2(to_move_4, 3, 5, 0);
|
||||
rassert(!strcmp(to_move_4, "?defgabcaa"));
|
||||
|
||||
// Test 5
|
||||
char to_move_5[] = "?defgabcaa";
|
||||
rstrmove2(to_move_5, 0, 5, 3);
|
||||
rassert(!strcmp(to_move_5, "abc?defgaa"));
|
||||
|
||||
// Test 6
|
||||
char to_move_6[] = "?defgabcaa";
|
||||
rstrmove2(to_move_6, 0, 5, 6);
|
||||
rassert(!strcmp(to_move_6, "abcaa?defg"));
|
||||
}
|
||||
|
||||
int main() {
|
||||
rtest_banner("rstring");
|
||||
rstring_test_whitespace();
|
||||
rstring_test_rstrtokline();
|
||||
rstring_test_rstrsort();
|
||||
rstring_test_rformat_number();
|
||||
rstring_test_rstraddslashes();
|
||||
rstring_test_rstrstripslashes();
|
||||
rstring_test_rstrstartswith();
|
||||
rstring_test_rstrendswith();
|
||||
rstring_test_rstrmove();
|
||||
return rtest_end("");
|
||||
}
|
439
rstring.h
Normal file
439
rstring.h
Normal file
@ -0,0 +1,439 @@
|
||||
#ifndef RSTRING_H
|
||||
#define RSTRING_H
|
||||
#include "rmalloc.h"
|
||||
#include "rtypes.h"
|
||||
#include "rmath.h"
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
char *rstrtimestamp() {
|
||||
time_t current_time;
|
||||
time(¤t_time);
|
||||
struct tm *local_time = localtime(¤t_time);
|
||||
static char time_string[100];
|
||||
time_string[0] = 0;
|
||||
strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", local_time);
|
||||
return time_string;
|
||||
}
|
||||
|
||||
ulonglong _r_generate_key_current = 0;
|
||||
|
||||
char *_rcat_int_int(int a, int b) {
|
||||
static char res[20];
|
||||
res[0] = 0;
|
||||
sprintf(res, "%d%d", a, b);
|
||||
return res;
|
||||
}
|
||||
char *_rcat_int_double(int a, double b) {
|
||||
static char res[20];
|
||||
res[0] = 0;
|
||||
sprintf(res, "%d%f", a, b);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *_rcat_charp_int(char *a, int b) {
|
||||
char res[20];
|
||||
sprintf(res, "%c", b);
|
||||
return strcat(a, res);
|
||||
}
|
||||
|
||||
char *_rcat_charp_double(char *a, double b) {
|
||||
char res[20];
|
||||
sprintf(res, "%f", b);
|
||||
return strcat(a, res);
|
||||
}
|
||||
|
||||
char *_rcat_charp_charp(char *a, char *b) {
|
||||
;
|
||||
return strcat(a, b);
|
||||
}
|
||||
char *_rcat_charp_char(char *a, char b) {
|
||||
char extra[] = {b, 0};
|
||||
return strcat(a, extra);
|
||||
}
|
||||
char *_rcat_charp_bool(char *a, bool *b) {
|
||||
if (b) {
|
||||
return strcat(a, "true");
|
||||
} else {
|
||||
return strcat(a, "false");
|
||||
}
|
||||
}
|
||||
|
||||
#define rcat(x, y) \
|
||||
_Generic((x), \
|
||||
int: _Generic((y), int: _rcat_int_int, double: _rcat_int_double, char *: _rcat_charp_charp), \
|
||||
char *: _Generic((y), \
|
||||
int: _rcat_charp_int, \
|
||||
double: _rcat_charp_double, \
|
||||
char *: _rcat_charp_charp, \
|
||||
char: _rcat_charp_char, \
|
||||
bool: _rcat_charp_bool))((x), (y))
|
||||
|
||||
char *rgenerate_key() {
|
||||
_r_generate_key_current++;
|
||||
static char key[100];
|
||||
key[0] = 0;
|
||||
sprintf(key, "%lld", _r_generate_key_current);
|
||||
return key;
|
||||
}
|
||||
|
||||
char *rformat_number(long long lnumber) {
|
||||
static char formatted[1024];
|
||||
|
||||
char number[1024] = {0};
|
||||
sprintf(number, "%lld", lnumber);
|
||||
|
||||
int len = strlen(number);
|
||||
int commas_needed = (len - 1) / 3;
|
||||
int new_len = len + commas_needed;
|
||||
|
||||
formatted[new_len] = '\0';
|
||||
|
||||
int i = len - 1;
|
||||
int j = new_len - 1;
|
||||
int count = 0;
|
||||
|
||||
while (i >= 0) {
|
||||
if (count == 3) {
|
||||
formatted[j--] = '.';
|
||||
count = 0;
|
||||
}
|
||||
formatted[j--] = number[i--];
|
||||
count++;
|
||||
}
|
||||
if (lnumber < 0)
|
||||
formatted[j--] = '-';
|
||||
return formatted;
|
||||
}
|
||||
|
||||
bool rstrextractdouble(char *str, double *d1) {
|
||||
for (size_t i = 0; i < strlen(str); i++) {
|
||||
if (isdigit(str[i])) {
|
||||
str += i;
|
||||
sscanf(str, "%lf", d1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void rstrstripslashes(const char *content, char *result) {
|
||||
size_t content_length = strlen((char *)content);
|
||||
unsigned int index = 0;
|
||||
for (unsigned int i = 0; i < content_length; i++) {
|
||||
char c = content[i];
|
||||
if (c == '\\') {
|
||||
i++;
|
||||
c = content[i];
|
||||
if (c == 'r') {
|
||||
c = '\r';
|
||||
} else if (c == 't') {
|
||||
c = '\t';
|
||||
} else if (c == 'b') {
|
||||
c = '\b';
|
||||
} else if (c == 'n') {
|
||||
c = '\n';
|
||||
} else if (c == 'f') {
|
||||
c = '\f';
|
||||
} else if (c == '\\') {
|
||||
// No need tbh
|
||||
c = '\\';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
result[index] = c;
|
||||
index++;
|
||||
}
|
||||
result[index] = 0;
|
||||
}
|
||||
|
||||
int rstrstartswith(const char *s1, const char *s2) {
|
||||
if (s1 == NULL)
|
||||
return s2 == NULL;
|
||||
if (s1 == s2 || s2 == NULL || *s2 == 0)
|
||||
return true;
|
||||
size_t len_s2 = strlen(s2);
|
||||
size_t len_s1 = strlen(s1);
|
||||
if (len_s2 > len_s1)
|
||||
return false;
|
||||
return !strncmp(s1, s2, len_s2);
|
||||
}
|
||||
|
||||
bool rstrendswith(const char *s1, const char *s2) {
|
||||
if (s1 == NULL)
|
||||
return s2 == NULL;
|
||||
if (s1 == s2 || s2 == NULL || *s2 == 0)
|
||||
return true;
|
||||
size_t len_s2 = strlen(s2);
|
||||
size_t len_s1 = strlen(s1);
|
||||
if (len_s2 > len_s1) {
|
||||
return false;
|
||||
}
|
||||
s1 += len_s1 - len_s2;
|
||||
return !strncmp(s1, s2, len_s2);
|
||||
}
|
||||
|
||||
void rstraddslashes(const char *content, char *result) {
|
||||
size_t content_length = strlen((char *)content);
|
||||
unsigned int index = 0;
|
||||
for (unsigned int i = 0; i < content_length; i++) {
|
||||
if (content[i] == '\r') {
|
||||
result[index] = '\\';
|
||||
index++;
|
||||
result[index] = 'r';
|
||||
index++;
|
||||
continue;
|
||||
} else if (content[i] == '\t') {
|
||||
result[index] = '\\';
|
||||
index++;
|
||||
result[index] = 't';
|
||||
index++;
|
||||
continue;
|
||||
} else if (content[i] == '\n') {
|
||||
result[index] = '\\';
|
||||
index++;
|
||||
result[index] = 'n';
|
||||
index++;
|
||||
continue;
|
||||
} else if (content[i] == '\\') {
|
||||
result[index] = '\\';
|
||||
index++;
|
||||
result[index] = '\\';
|
||||
index++;
|
||||
continue;
|
||||
} else if (content[i] == '\b') {
|
||||
result[index] = '\\';
|
||||
index++;
|
||||
result[index] = 'b';
|
||||
index++;
|
||||
continue;
|
||||
} else if (content[i] == '\f') {
|
||||
result[index] = '\\';
|
||||
index++;
|
||||
result[index] = 'f';
|
||||
index++;
|
||||
continue;
|
||||
} else if (content[i] == '"') {
|
||||
result[index] = '\\';
|
||||
index++;
|
||||
result[index] = '"';
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
result[index] = content[i];
|
||||
index++;
|
||||
result[index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int rstrip_whitespace(char *input, char *output) {
|
||||
output[0] = 0;
|
||||
int count = 0;
|
||||
size_t len = strlen(input);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (input[i] == '\t' || input[i] == ' ' || input[i] == '\n') {
|
||||
continue;
|
||||
}
|
||||
count = i;
|
||||
size_t j;
|
||||
for (j = 0; j < len - count; j++) {
|
||||
output[j] = input[j + count];
|
||||
}
|
||||
output[j] = '\0';
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts "pony" to \"pony\". Addslashes does not
|
||||
* Converts "pony\npony" to "pony\n"
|
||||
* "pony"
|
||||
*/
|
||||
void rstrtocstring(const char *input, char *output) {
|
||||
int index = 0;
|
||||
char clean_input[strlen(input) * 2];
|
||||
char *iptr = clean_input;
|
||||
rstraddslashes(input, clean_input);
|
||||
output[index] = '"';
|
||||
index++;
|
||||
while (*iptr) {
|
||||
if (*iptr == '"') {
|
||||
output[index] = '\\';
|
||||
output++;
|
||||
} else if (*iptr == '\\' && *(iptr + 1) == 'n') {
|
||||
output[index] = '\\';
|
||||
output++;
|
||||
output[index] = 'n';
|
||||
output++;
|
||||
output[index] = '"';
|
||||
output++;
|
||||
output[index] = '\n';
|
||||
output++;
|
||||
output[index] = '"';
|
||||
output++;
|
||||
iptr++;
|
||||
iptr++;
|
||||
continue;
|
||||
}
|
||||
output[index] = *iptr;
|
||||
index++;
|
||||
iptr++;
|
||||
}
|
||||
if (output[index - 1] == '"' && output[index - 2] == '\n') {
|
||||
output[index - 1] = 0;
|
||||
} else if (output[index - 1] != '"') {
|
||||
output[index] = '"';
|
||||
output[index + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t rstrtokline(char *input, char *output, size_t offset, bool strip_nl) {
|
||||
|
||||
size_t len = strlen(input);
|
||||
output[0] = 0;
|
||||
size_t new_offset = 0;
|
||||
size_t j;
|
||||
size_t index = 0;
|
||||
|
||||
for (j = offset; j < len + offset; j++) {
|
||||
if (input[j] == 0) {
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
index = j - offset;
|
||||
output[index] = input[j];
|
||||
|
||||
if (output[index] == '\n') {
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
output[index] = 0;
|
||||
|
||||
new_offset = index + offset;
|
||||
|
||||
if (strip_nl) {
|
||||
if (output[index - 1] == '\n') {
|
||||
output[index - 1] = 0;
|
||||
}
|
||||
}
|
||||
return new_offset;
|
||||
}
|
||||
|
||||
void rstrjoin(char **lines, size_t count, char *glue, char *output) {
|
||||
output[0] = 0;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
strcat(output, lines[i]);
|
||||
if (i != count - 1)
|
||||
strcat(output, glue);
|
||||
}
|
||||
}
|
||||
|
||||
int rstrsplit(char *input, char **lines) {
|
||||
int index = 0;
|
||||
size_t offset = 0;
|
||||
char line[1024];
|
||||
while ((offset = rstrtokline(input, line, offset, false)) && *line) {
|
||||
if (!*line) {
|
||||
break;
|
||||
}
|
||||
lines[index] = (char *)malloc(strlen(line) + 1);
|
||||
strcpy(lines[index], line);
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
bool rstartswithnumber(char *str) { return isdigit(str[0]); }
|
||||
|
||||
void rstrmove2(char *str, unsigned int start, size_t length, unsigned int new_pos) {
|
||||
size_t str_len = strlen(str);
|
||||
char new_str[str_len + 1];
|
||||
memset(new_str, 0, str_len);
|
||||
if (start < new_pos) {
|
||||
strncat(new_str, str + length, str_len - length - start);
|
||||
new_str[new_pos] = 0;
|
||||
strncat(new_str, str + start, length);
|
||||
strcat(new_str, str + strlen(new_str));
|
||||
memset(str, 0, str_len);
|
||||
strcpy(str, new_str);
|
||||
} else {
|
||||
strncat(new_str, str + start, length);
|
||||
strncat(new_str, str, start);
|
||||
strncat(new_str, str + start + length, str_len - start);
|
||||
memset(str, 0, str_len);
|
||||
strcpy(str, new_str);
|
||||
}
|
||||
new_str[str_len] = 0;
|
||||
}
|
||||
|
||||
void rstrmove(char *str, unsigned int start, size_t length, unsigned int new_pos) {
|
||||
size_t str_len = strlen(str);
|
||||
if (start >= str_len || new_pos >= str_len || start + length > str_len) {
|
||||
return;
|
||||
}
|
||||
char temp[length + 1];
|
||||
strncpy(temp, str + start, length);
|
||||
temp[length] = 0;
|
||||
if (start < new_pos) {
|
||||
memmove(str + start, str + start + length, new_pos - start);
|
||||
strncpy(str + new_pos - length + 1, temp, length);
|
||||
} else {
|
||||
memmove(str + new_pos + length, str + new_pos, start - new_pos);
|
||||
strncpy(str + new_pos, temp, length);
|
||||
}
|
||||
}
|
||||
|
||||
int cmp_line(const void *left, const void *right) {
|
||||
char *l = *(char **)left;
|
||||
char *r = *(char **)right;
|
||||
|
||||
char lstripped[strlen(l) + 1];
|
||||
rstrip_whitespace(l, lstripped);
|
||||
char rstripped[strlen(r) + 1];
|
||||
rstrip_whitespace(r, rstripped);
|
||||
|
||||
double d1, d2;
|
||||
bool found_d1 = rstrextractdouble(lstripped, &d1);
|
||||
bool found_d2 = rstrextractdouble(rstripped, &d2);
|
||||
|
||||
if (found_d1 && found_d2) {
|
||||
double frac_part1;
|
||||
double int_part1;
|
||||
frac_part1 = modf(d1, &int_part1);
|
||||
double frac_part2;
|
||||
double int_part2;
|
||||
frac_part2 = modf(d2, &int_part2);
|
||||
if (d1 == d2) {
|
||||
return strcmp(lstripped, rstripped);
|
||||
} else if (frac_part1 && frac_part2) {
|
||||
return d1 > d2;
|
||||
} else if (frac_part1 && !frac_part2) {
|
||||
return 1;
|
||||
} else if (frac_part2 && !frac_part1) {
|
||||
return -1;
|
||||
} else if (!frac_part1 && !frac_part2) {
|
||||
return d1 > d2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rstrsort(char *input, char *output) {
|
||||
char **lines = (char **)malloc(strlen(input) * 10);
|
||||
int line_count = rstrsplit(input, lines);
|
||||
qsort(lines, line_count, sizeof(char *), cmp_line);
|
||||
rstrjoin(lines, line_count, "", output);
|
||||
for (int i = 0; i < line_count; i++) {
|
||||
free(lines[i]);
|
||||
}
|
||||
free(lines);
|
||||
return line_count;
|
||||
}
|
||||
|
||||
#endif
|
27
rstring_list.c
Normal file
27
rstring_list.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include "rstring_list.h"
|
||||
#include "rtest.h"
|
||||
|
||||
void test_rstring_list() {
|
||||
rtest_banner("new");
|
||||
rstring_list_t *rsl = rstring_list_new();
|
||||
rassert(rsl->count == 0);
|
||||
rassert(rsl->count == 0);
|
||||
rtest_banner("add");
|
||||
rstring_list_add(rsl, "test1");
|
||||
rassert(rsl->count == 1);
|
||||
rassert(rsl->count == 1);
|
||||
rstring_list_add(rsl, "test2");
|
||||
rassert(rsl->count == 2);
|
||||
rassert(rsl->count == 2);
|
||||
rtest_banner("contains");
|
||||
rassert(rstring_list_contains(rsl, "test1"));
|
||||
rassert(rstring_list_contains(rsl, "test2"));
|
||||
rassert(!rstring_list_contains(rsl, "test3"));
|
||||
rtest_banner("free");
|
||||
rstring_list_free(rsl);
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_rstring_list();
|
||||
return rtest_end("");
|
||||
}
|
47
rstring_list.h
Normal file
47
rstring_list.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef RSTRING_LIST_H
|
||||
#define RSTRING_LIST_H
|
||||
#include "rmalloc.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct rstring_list_t {
|
||||
unsigned int size;
|
||||
unsigned int count;
|
||||
char **strings;
|
||||
} rstring_list_t;
|
||||
|
||||
rstring_list_t *rstring_list_new() {
|
||||
rstring_list_t *rsl = (rstring_list_t *)malloc(sizeof(rstring_list_t));
|
||||
memset(rsl, 0, sizeof(rstring_list_t));
|
||||
return rsl;
|
||||
}
|
||||
|
||||
void rstring_list_free(rstring_list_t *rsl) {
|
||||
for (unsigned int i = 0; i < rsl->size; i++) {
|
||||
free(rsl->strings[i]);
|
||||
}
|
||||
if (rsl->strings)
|
||||
free(rsl->strings);
|
||||
free(rsl);
|
||||
rsl = NULL;
|
||||
}
|
||||
|
||||
void rstring_list_add(rstring_list_t *rsl, char *str) {
|
||||
if (rsl->count == rsl->size) {
|
||||
rsl->size++;
|
||||
|
||||
rsl->strings = (char **)realloc(rsl->strings, sizeof(char *) * rsl->size);
|
||||
}
|
||||
rsl->strings[rsl->count] = strdup(str);
|
||||
rsl->count++;
|
||||
}
|
||||
bool rstring_list_contains(rstring_list_t *rsl, char *str) {
|
||||
for (unsigned int i = 0; i < rsl->count; i++) {
|
||||
if (!strcmp(rsl->strings[i], str))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
38
rtemp.c
Normal file
38
rtemp.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include "rtemp.h"
|
||||
#include "rtest.h"
|
||||
#include <string.h>
|
||||
|
||||
char *classic(char *content) {
|
||||
sstring(result, 1024);
|
||||
strcpy(result, content);
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rtemp_test_rtempc() {
|
||||
rtest_banner("rtempc");
|
||||
char *res1 = sbuf("test1");
|
||||
char *res2 = sbuf("test2");
|
||||
char *res3 = sbuf("test3");
|
||||
char *res4 = sbuf("test4");
|
||||
char *res5 = sbuf("test5");
|
||||
rassert(!strcmp(res5, "test5"));
|
||||
rassert(!strcmp(res4, "test4"));
|
||||
rassert(!strcmp(res3, "test3"));
|
||||
rassert(!strcmp(res2, "test2"));
|
||||
rassert(!strcmp(res1, "test1"));
|
||||
char line[1024] = {0};
|
||||
sprintf(line, "%s%s%s", rtempc("test1"), rtempc("test2"), rtempc("test3"));
|
||||
rassert(!strcmp(line, "test1test2test3"));
|
||||
line[0] = 0;
|
||||
sprintf(line, "%s%s%s", classic("test1"), classic("test2"), classic("test3"));
|
||||
rassert(strcmp(line, "test1test2test3"));
|
||||
}
|
||||
|
||||
int main() {
|
||||
rtest_banner("rtemp");
|
||||
|
||||
rtemp_test_rtempc();
|
||||
|
||||
return rtest_end("");
|
||||
}
|
53
rtemp.h
Normal file
53
rtemp.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef RTEMP_H
|
||||
#define RTEMP_H
|
||||
#include "rtypes.h"
|
||||
#include <pthread.h>
|
||||
#ifndef RTEMPC_SLOT_COUNT
|
||||
#define RTEMPC_SLOT_COUNT 20
|
||||
#endif
|
||||
#ifndef RTEMPC_SLOT_SIZE
|
||||
#define RTEMPC_SLOT_SIZE 1024 * 64 * 128
|
||||
#endif
|
||||
|
||||
bool _rtempc_initialized = 0;
|
||||
pthread_mutex_t _rtempc_thread_lock;
|
||||
bool rtempc_use_mutex = true;
|
||||
byte _current_rtempc_slot = 1;
|
||||
char _rtempc_buffer[RTEMPC_SLOT_COUNT][RTEMPC_SLOT_SIZE];
|
||||
char *rtempc(char *data) {
|
||||
|
||||
if (rtempc_use_mutex) {
|
||||
if (!_rtempc_initialized) {
|
||||
_rtempc_initialized = true;
|
||||
pthread_mutex_init(&_rtempc_thread_lock, NULL);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&_rtempc_thread_lock);
|
||||
}
|
||||
|
||||
uint current_rtempc_slot = _current_rtempc_slot;
|
||||
_rtempc_buffer[current_rtempc_slot][0] = 0;
|
||||
strcpy(_rtempc_buffer[current_rtempc_slot], data);
|
||||
_current_rtempc_slot++;
|
||||
if (_current_rtempc_slot == RTEMPC_SLOT_COUNT) {
|
||||
_current_rtempc_slot = 0;
|
||||
}
|
||||
if (rtempc_use_mutex)
|
||||
pthread_mutex_unlock(&_rtempc_thread_lock);
|
||||
return _rtempc_buffer[current_rtempc_slot];
|
||||
}
|
||||
|
||||
#define sstring(_pname, _psize) \
|
||||
static char _##_pname[_psize]; \
|
||||
_##_pname[0] = 0; \
|
||||
char *_pname = _##_pname;
|
||||
|
||||
#define string(_pname, _psize) \
|
||||
char _##_pname[_psize]; \
|
||||
_##_pname[0] = 0; \
|
||||
char *_pname = _##_pname;
|
||||
|
||||
#define sreset(_pname, _psize) _pname = _##_pname;
|
||||
|
||||
#define sbuf(val) rtempc(val)
|
||||
#endif
|
34
rterm.c
Normal file
34
rterm.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include "rterm.h"
|
||||
|
||||
void before_cursor_move(rterm_t *rterm) {
|
||||
// printf("Before cursor update: %d:%d\n",rterm->cursor.x,rterm->cursor.y);
|
||||
}
|
||||
void after_cursor_move(rterm_t *rterm) {
|
||||
|
||||
// rterm->cursor.x++;
|
||||
}
|
||||
void before_key_press(rterm_t *rterm) {
|
||||
|
||||
// if(rterm->key.c == 65 && rterm->key.escape){
|
||||
// rterm->key.c = 66;
|
||||
//}
|
||||
}
|
||||
void tick(rterm_t *rt) {
|
||||
static char status_text[1024];
|
||||
status_text[0] = 0;
|
||||
sprintf(status_text, "\rp:%d:%d | k:%c:%d | i:%ld ", rt->cursor.x + 1, rt->cursor.y + 1, rt->key.c == 0 ? '0' : rt->key.c, rt->key.c,
|
||||
rt->iterations);
|
||||
rt->status_text = status_text;
|
||||
}
|
||||
|
||||
int main() {
|
||||
rterm_t rt;
|
||||
rterm_init(&rt);
|
||||
rt.show_cursor = true;
|
||||
rt.before_key_press = before_key_press;
|
||||
rt.before_cursor_move = before_cursor_move;
|
||||
rt.after_cursor_move = after_cursor_move;
|
||||
rt.tick = tick;
|
||||
rterm_loop(&rt);
|
||||
return 0;
|
||||
}
|
303
rterm.h
Normal file
303
rterm.h
Normal file
@ -0,0 +1,303 @@
|
||||
#ifndef RTERM_H
|
||||
#define RTERM_H
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "rio.h"
|
||||
#include "rtime.h"
|
||||
typedef struct winsize winsize_t;
|
||||
|
||||
typedef struct rshell_keypress_t {
|
||||
bool pressed;
|
||||
bool ctrl;
|
||||
bool shift;
|
||||
bool escape;
|
||||
char c;
|
||||
int ms;
|
||||
int fd;
|
||||
} rshell_keypress_t;
|
||||
|
||||
typedef struct rterm_t {
|
||||
bool show_cursor;
|
||||
bool show_footer;
|
||||
int ms_tick;
|
||||
rshell_keypress_t key;
|
||||
void (*before_cursor_move)(struct rterm_t *);
|
||||
void (*after_cursor_move)(struct rterm_t *);
|
||||
void (*after_key_press)(struct rterm_t *);
|
||||
void (*before_key_press)(struct rterm_t *);
|
||||
void (*before_draw)(struct rterm_t *);
|
||||
void (*after_draw)(struct rterm_t *);
|
||||
void *session;
|
||||
unsigned long iterations;
|
||||
void (*tick)(struct rterm_t *);
|
||||
char *status_text;
|
||||
char *_status_text_previous;
|
||||
winsize_t size;
|
||||
struct {
|
||||
int x;
|
||||
int y;
|
||||
int pos;
|
||||
int available;
|
||||
} cursor;
|
||||
} rterm_t;
|
||||
|
||||
typedef void (*rterm_event)(rterm_t *);
|
||||
|
||||
void rterm_init(rterm_t *rterm) {
|
||||
memset(rterm, 0, sizeof(rterm_t));
|
||||
rterm->show_cursor = true;
|
||||
rterm->cursor.x = 0;
|
||||
rterm->cursor.y = 0;
|
||||
rterm->ms_tick = 100;
|
||||
rterm->_status_text_previous = NULL;
|
||||
}
|
||||
|
||||
void rterm_getwinsize(winsize_t *w) {
|
||||
// Get the terminal size
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, w) == -1) {
|
||||
perror("ioctl");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void rrawfd(int fd) {
|
||||
struct termios orig_termios;
|
||||
tcgetattr(fd, &orig_termios); // Get current terminal attributes
|
||||
|
||||
struct termios raw = orig_termios;
|
||||
raw.c_lflag &= ~(ICANON | ISIG | ECHO); // ECHO // Disable canonical mode and echoing
|
||||
raw.c_cc[VMIN] = 1;
|
||||
raw.c_cc[VTIME] = 240; // Set timeout for read input
|
||||
|
||||
tcsetattr(fd, TCSAFLUSH, &raw);
|
||||
}
|
||||
|
||||
// Terminal setup functions
|
||||
void enableRawMode(struct termios *orig_termios) {
|
||||
|
||||
struct termios raw = *orig_termios;
|
||||
raw.c_lflag &= ~(ICANON | ECHO); // Disable canonical mode and echoing
|
||||
raw.c_cc[VMIN] = 1;
|
||||
raw.c_cc[VTIME] = 240; // Set timeout for read input
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||
}
|
||||
|
||||
void disableRawMode(struct termios *orig_termios) {
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH,
|
||||
orig_termios); // Restore original terminal settings
|
||||
}
|
||||
|
||||
void rterm_clear_screen() {
|
||||
printf("\x1b[2J"); // Clear the entire screen
|
||||
printf("\x1b[H"); // Move cursor to the home position (0,0)
|
||||
}
|
||||
|
||||
void setBackgroundColor() {
|
||||
printf("\x1b[34m"); // Set background color to blue
|
||||
}
|
||||
|
||||
void rterm_move_cursor(int x, int y) {
|
||||
|
||||
printf("\x1b[%d;%dH", y + 1, x + 1); // Move cursor to (x, y)
|
||||
}
|
||||
|
||||
void cursor_set(rterm_t *rt, int x, int y) {
|
||||
rt->cursor.x = x;
|
||||
rt->cursor.y = y;
|
||||
rt->cursor.pos = y * rt->size.ws_col + x;
|
||||
rterm_move_cursor(rt->cursor.x, rt->cursor.y);
|
||||
}
|
||||
void cursor_restore(rterm_t *rt) { rterm_move_cursor(rt->cursor.x, rt->cursor.y); }
|
||||
|
||||
void rterm_print_status_bar(rterm_t *rt, char c, unsigned long i) {
|
||||
if (rt->_status_text_previous && !strcmp(rt->_status_text_previous, rt->status_text)) {
|
||||
return;
|
||||
}
|
||||
if (rt->_status_text_previous) {
|
||||
free(rt->_status_text_previous);
|
||||
}
|
||||
rt->_status_text_previous = strdup(rt->status_text);
|
||||
winsize_t ws = rt->size;
|
||||
cursor_set(rt, rt->cursor.x, rt->cursor.y);
|
||||
rterm_move_cursor(0, ws.ws_row - 1);
|
||||
|
||||
char output_str[1024];
|
||||
output_str[0] = 0;
|
||||
|
||||
// strcat(output_str, "\x1b[48;5;240m");
|
||||
|
||||
for (int i = 0; i < ws.ws_col; i++) {
|
||||
strcat(output_str, " ");
|
||||
}
|
||||
char content[500];
|
||||
content[0] = 0;
|
||||
if (!rt->status_text) {
|
||||
sprintf(content, "\rp:%d:%d | k:%c:%d | i:%ld ", rt->cursor.x + 1, rt->cursor.y + 1, c == 0 ? '0' : c, c, i);
|
||||
} else {
|
||||
sprintf(content, "\r%s", rt->status_text);
|
||||
}
|
||||
strcat(output_str, content);
|
||||
// strcat(output_str, "\x1b[0m");
|
||||
printf("%s", output_str);
|
||||
cursor_restore(rt);
|
||||
}
|
||||
|
||||
void rterm_show_cursor() {
|
||||
printf("\x1b[?25h"); // Show the cursor
|
||||
}
|
||||
|
||||
void rterm_hide_cursor() {
|
||||
printf("\x1b[?25l"); // Hide the cursor
|
||||
}
|
||||
|
||||
rshell_keypress_t rshell_getkey(rterm_t *rt) {
|
||||
static rshell_keypress_t press;
|
||||
press.c = 0;
|
||||
press.ctrl = false;
|
||||
press.shift = false;
|
||||
press.escape = false;
|
||||
press.pressed = rfd_wait(0, rt->ms_tick);
|
||||
if (!press.pressed) {
|
||||
return press;
|
||||
}
|
||||
press.c = getchar();
|
||||
char ch = press.c;
|
||||
if (ch == '\x1b') {
|
||||
// Get detail
|
||||
ch = getchar();
|
||||
|
||||
if (ch == '[') {
|
||||
// non char key:
|
||||
press.escape = true;
|
||||
|
||||
ch = getchar(); // is a number. 1 if shift + arrow
|
||||
press.c = ch;
|
||||
if (ch >= '0' && ch <= '9')
|
||||
ch = getchar();
|
||||
press.c = ch;
|
||||
if (ch == ';') {
|
||||
ch = getchar();
|
||||
press.c = ch;
|
||||
if (ch == '5') {
|
||||
press.ctrl = true;
|
||||
press.c = getchar(); // De arrow
|
||||
}
|
||||
}
|
||||
} else if (ch == 27) {
|
||||
press.escape = true;
|
||||
press.c = ch;
|
||||
} else {
|
||||
press.c = ch;
|
||||
}
|
||||
}
|
||||
return press;
|
||||
}
|
||||
|
||||
// Main function
|
||||
void rterm_loop(rterm_t *rt) {
|
||||
struct termios orig_termios;
|
||||
tcgetattr(STDIN_FILENO, &orig_termios); // Get current terminal attributes
|
||||
enableRawMode(&orig_termios);
|
||||
|
||||
int x = 0, y = 0; // Initial cursor position
|
||||
char ch = 0;
|
||||
|
||||
;
|
||||
while (1) {
|
||||
rterm_getwinsize(&rt->size);
|
||||
rt->cursor.available = rt->size.ws_col * rt->size.ws_row;
|
||||
if (rt->tick) {
|
||||
rt->tick(rt);
|
||||
}
|
||||
|
||||
rterm_hide_cursor();
|
||||
setBackgroundColor();
|
||||
rterm_clear_screen();
|
||||
if (rt->before_draw) {
|
||||
rt->before_draw(rt);
|
||||
}
|
||||
rterm_print_status_bar(rt, ch, rt->iterations);
|
||||
if (rt->after_draw) {
|
||||
rt->after_draw(rt);
|
||||
}
|
||||
if (!rt->iterations || (x != rt->cursor.x || y != rt->cursor.y)) {
|
||||
if (rt->cursor.y == rt->size.ws_row) {
|
||||
rt->cursor.y--;
|
||||
}
|
||||
if (rt->cursor.y < 0) {
|
||||
rt->cursor.y = 0;
|
||||
}
|
||||
x = rt->cursor.x;
|
||||
y = rt->cursor.y;
|
||||
if (rt->before_cursor_move)
|
||||
rt->before_cursor_move(rt);
|
||||
cursor_set(rt, rt->cursor.x, rt->cursor.y);
|
||||
if (rt->after_cursor_move)
|
||||
rt->after_cursor_move(rt);
|
||||
// x = rt->cursor.x;
|
||||
// y = rt->cursor.y;
|
||||
}
|
||||
if (rt->show_cursor)
|
||||
rterm_show_cursor();
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
rt->key = rshell_getkey(rt);
|
||||
if (rt->key.pressed && rt->before_key_press) {
|
||||
rt->before_key_press(rt);
|
||||
}
|
||||
rshell_keypress_t key = rt->key;
|
||||
ch = key.c;
|
||||
if (ch == 'q')
|
||||
break; // Press 'q' to quit
|
||||
if (key.c == -1) {
|
||||
nsleep(1000 * 1000);
|
||||
}
|
||||
// Escape
|
||||
if (key.escape) {
|
||||
switch (key.c) {
|
||||
case 65: // Move up
|
||||
if (rt->cursor.y > -1)
|
||||
rt->cursor.y--;
|
||||
break;
|
||||
case 66: // Move down
|
||||
if (rt->cursor.y < rt->size.ws_row)
|
||||
rt->cursor.y++;
|
||||
break;
|
||||
case 68: // Move left
|
||||
if (rt->cursor.x > 0)
|
||||
rt->cursor.x--;
|
||||
if (key.ctrl)
|
||||
rt->cursor.x -= 4;
|
||||
break;
|
||||
case 67: // Move right
|
||||
if (rt->cursor.x < rt->size.ws_col) {
|
||||
rt->cursor.x++;
|
||||
}
|
||||
if (key.ctrl) {
|
||||
rt->cursor.x += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rt->key.pressed && rt->after_key_press) {
|
||||
rt->after_key_press(rt);
|
||||
}
|
||||
rt->iterations++;
|
||||
|
||||
// usleep (1000);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
printf("\x1b[0m"); // Reset colors
|
||||
rterm_clear_screen();
|
||||
disableRawMode(&orig_termios);
|
||||
}
|
||||
#endif
|
4
rterminal.c
Normal file
4
rterminal.c
Normal file
@ -0,0 +1,4 @@
|
||||
#include "rterminal.h"
|
||||
#include "rtest.h"
|
||||
|
||||
int main() { rlib_test_progressbar(); }
|
189
rterminal.h
Normal file
189
rterminal.h
Normal file
@ -0,0 +1,189 @@
|
||||
#ifndef RLIB_TERMINAL_H
|
||||
#define RLIB_TERMINAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "rtest.h"
|
||||
|
||||
char *rfcaptured = NULL;
|
||||
|
||||
void rfcapture(FILE *f, char *buff, size_t size) {
|
||||
rfcaptured = buff;
|
||||
setvbuf(f, rfcaptured, _IOFBF, size);
|
||||
}
|
||||
void rfstopcapture(FILE *f) { setvbuf(f, 0, _IOFBF, 0); }
|
||||
|
||||
bool _r_disable_stdout_toggle = false;
|
||||
|
||||
FILE *_r_original_stdout = NULL;
|
||||
|
||||
bool rr_enable_stdout() {
|
||||
if (_r_disable_stdout_toggle)
|
||||
return false;
|
||||
if (!_r_original_stdout) {
|
||||
stdout = fopen("/dev/null", "rb");
|
||||
return false;
|
||||
}
|
||||
if (_r_original_stdout && _r_original_stdout != stdout) {
|
||||
fclose(stdout);
|
||||
}
|
||||
stdout = _r_original_stdout;
|
||||
return true;
|
||||
}
|
||||
bool rr_disable_stdout() {
|
||||
if (_r_disable_stdout_toggle) {
|
||||
return false;
|
||||
}
|
||||
if (_r_original_stdout == NULL) {
|
||||
_r_original_stdout = stdout;
|
||||
}
|
||||
if (stdout == _r_original_stdout) {
|
||||
stdout = fopen("/dev/null", "rb");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool rr_toggle_stdout() {
|
||||
if (!_r_original_stdout) {
|
||||
rr_disable_stdout();
|
||||
return true;
|
||||
} else if (stdout != _r_original_stdout) {
|
||||
rr_enable_stdout();
|
||||
return true;
|
||||
} else {
|
||||
rr_disable_stdout();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct rprogressbar_t {
|
||||
unsigned long current_value;
|
||||
unsigned long min_value;
|
||||
unsigned long max_value;
|
||||
unsigned int length;
|
||||
bool changed;
|
||||
double percentage;
|
||||
unsigned int width;
|
||||
unsigned long draws;
|
||||
FILE *fout;
|
||||
} rprogressbar_t;
|
||||
|
||||
rprogressbar_t *rprogressbar_new(long min_value, long max_value, unsigned int width, FILE *fout) {
|
||||
rprogressbar_t *pbar = (rprogressbar_t *)malloc(sizeof(rprogressbar_t));
|
||||
pbar->min_value = min_value;
|
||||
pbar->max_value = max_value;
|
||||
pbar->current_value = min_value;
|
||||
pbar->width = width;
|
||||
pbar->draws = 0;
|
||||
pbar->length = 0;
|
||||
pbar->changed = false;
|
||||
pbar->fout = fout ? fout : stdout;
|
||||
return pbar;
|
||||
}
|
||||
|
||||
void rprogressbar_free(rprogressbar_t *pbar) { free(pbar); }
|
||||
|
||||
void rprogressbar_draw(rprogressbar_t *pbar) {
|
||||
if (!pbar->changed) {
|
||||
return;
|
||||
} else {
|
||||
pbar->changed = false;
|
||||
}
|
||||
pbar->draws++;
|
||||
char draws_text[22];
|
||||
draws_text[0] = 0;
|
||||
sprintf(draws_text, "%ld", pbar->draws);
|
||||
char *draws_textp = draws_text;
|
||||
// bool draws_text_len = strlen(draws_text);
|
||||
char bar_begin_char = ' ';
|
||||
char bar_progress_char = ' ';
|
||||
char bar_empty_char = ' ';
|
||||
char bar_end_char = ' ';
|
||||
char content[4096] = {0};
|
||||
char bar_content[1024];
|
||||
char buff[2048] = {0};
|
||||
bar_content[0] = '\r';
|
||||
bar_content[1] = bar_begin_char;
|
||||
unsigned int index = 2;
|
||||
for (unsigned long i = 0; i < pbar->length; i++) {
|
||||
if (*draws_textp) {
|
||||
bar_content[index] = *draws_textp;
|
||||
draws_textp++;
|
||||
} else {
|
||||
bar_content[index] = bar_progress_char;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
char infix[] = "\033[0m";
|
||||
for (unsigned long i = 0; i < strlen(infix); i++) {
|
||||
bar_content[index] = infix[i];
|
||||
index++;
|
||||
}
|
||||
for (unsigned long i = 0; i < pbar->width - pbar->length; i++) {
|
||||
bar_content[index] = bar_empty_char;
|
||||
index++;
|
||||
}
|
||||
bar_content[index] = bar_end_char;
|
||||
bar_content[index + 1] = '\0';
|
||||
sprintf(buff, "\033[43m%s\033[0m \033[33m%.2f%%\033[0m ", bar_content, pbar->percentage * 100);
|
||||
strcat(content, buff);
|
||||
if (pbar->width == pbar->length) {
|
||||
strcat(content, "\r");
|
||||
for (unsigned long i = 0; i < pbar->width + 10; i++) {
|
||||
strcat(content, " ");
|
||||
}
|
||||
strcat(content, "\r");
|
||||
}
|
||||
fprintf(pbar->fout, "%s", content);
|
||||
fflush(pbar->fout);
|
||||
}
|
||||
|
||||
bool rprogressbar_update(rprogressbar_t *pbar, unsigned long value) {
|
||||
if (value == pbar->current_value) {
|
||||
return false;
|
||||
}
|
||||
pbar->current_value = value;
|
||||
pbar->percentage = (double)pbar->current_value / (double)(pbar->max_value - pbar->min_value);
|
||||
unsigned long new_length = (unsigned long)(pbar->percentage * pbar->width);
|
||||
pbar->changed = new_length != pbar->length;
|
||||
if (pbar->changed) {
|
||||
pbar->length = new_length;
|
||||
rprogressbar_draw(pbar);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t rreadline(char *data, size_t len, bool strip_ln) {
|
||||
__attribute__((unused)) char *unused = fgets(data, len, stdin);
|
||||
size_t length = strlen(data);
|
||||
if (length && strip_ln)
|
||||
data[length - 1] = 0;
|
||||
return length;
|
||||
}
|
||||
|
||||
void rlib_test_progressbar() {
|
||||
rtest_banner("Progress bar");
|
||||
rprogressbar_t *pbar = rprogressbar_new(0, 1000, 10, stderr);
|
||||
rprogressbar_draw(pbar);
|
||||
// No draws executed, nothing to show
|
||||
rassert(pbar->draws == 0);
|
||||
rprogressbar_update(pbar, 500);
|
||||
rassert(pbar->percentage == 0.5);
|
||||
rprogressbar_update(pbar, 500);
|
||||
rprogressbar_update(pbar, 501);
|
||||
rprogressbar_update(pbar, 502);
|
||||
// Should only have drawn one time since value did change, but percentage
|
||||
// did not
|
||||
rassert(pbar->draws == 1);
|
||||
// Changed is false because update function calls draw
|
||||
rassert(pbar->changed == false);
|
||||
rprogressbar_update(pbar, 777);
|
||||
rassert(pbar->percentage == 0.777);
|
||||
rprogressbar_update(pbar, 1000);
|
||||
rassert(pbar->percentage == 1);
|
||||
}
|
||||
|
||||
#endif
|
122
rtest.h
Normal file
122
rtest.h
Normal file
@ -0,0 +1,122 @@
|
||||
#ifndef RTEST_H
|
||||
#define RTEST_H
|
||||
#include "remo.h"
|
||||
#include "rmalloc.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "rprint.h"
|
||||
#define debug(fmt, ...) printf("%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__);
|
||||
|
||||
char *rcurrent_banner;
|
||||
int rassert_count = 0;
|
||||
unsigned short rtest_is_first = 1;
|
||||
unsigned int rtest_fail_count = 0;
|
||||
|
||||
int rtest_end(char *content) {
|
||||
// Returns application exit code. 0 == success
|
||||
printf("%s", content);
|
||||
printf("\n@assertions: %d\n", rassert_count);
|
||||
printf("@memory: %s%s\n", rmalloc_stats(), rmalloc_count == 0 ? remo_get("rainbow") : "fire");
|
||||
|
||||
if (rmalloc_count != 0) {
|
||||
printf("MEMORY ERROR %s\n", remo_get("cross mark"));
|
||||
return rtest_fail_count > 0;
|
||||
}
|
||||
return rtest_fail_count > 0;
|
||||
}
|
||||
|
||||
void rtest_test_banner(char *content, char *file) {
|
||||
if (rtest_is_first == 1) {
|
||||
char delimiter[] = ".";
|
||||
char *d = delimiter;
|
||||
char f[2048];
|
||||
strcpy(f, file);
|
||||
printf("%s tests", strtok(f, d));
|
||||
rtest_is_first = 0;
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
}
|
||||
printf("\n - %s ", content);
|
||||
}
|
||||
|
||||
bool rtest_test_true_silent(char *expr, int res, int line) {
|
||||
rassert_count++;
|
||||
if (res) {
|
||||
return true;
|
||||
}
|
||||
rprintrf(stderr, "\nERROR on line %d: %s", line, expr);
|
||||
rtest_fail_count++;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rtest_test_true(char *expr, int res, int line) {
|
||||
rassert_count++;
|
||||
if (res) {
|
||||
fprintf(stdout, "%s", remo_get("Slightly Smiling Face"));
|
||||
return true;
|
||||
}
|
||||
rprintrf(stderr, "\nERROR %s on line %d: %s\n", remo_get("skull"), line, expr);
|
||||
rtest_fail_count++;
|
||||
return false;
|
||||
}
|
||||
bool rtest_test_false_silent(char *expr, int res, int line) { return rtest_test_true_silent(expr, !res, line); }
|
||||
bool rtest_test_false(char *expr, int res, int line) { return rtest_test_true(expr, !res, line); }
|
||||
void rtest_test_skip(char *expr, int line) { rprintgf(stderr, "\n @skip(%s) on line %d\n", expr, line); }
|
||||
void rtest_test_assert(char *expr, int res, int line) {
|
||||
if (rtest_test_true(expr, res, line)) {
|
||||
return;
|
||||
}
|
||||
rtest_end("");
|
||||
exit(40);
|
||||
}
|
||||
|
||||
#define rtest_banner(content) \
|
||||
rcurrent_banner = content; \
|
||||
rtest_test_banner(content, __FILE__);
|
||||
#define rtest_true(expr) rtest_test_true(#expr, expr, __LINE__);
|
||||
#define rtest_assert(expr) \
|
||||
{ \
|
||||
int __valid = expr ? 1 : 0; \
|
||||
rtest_test_true(#expr, __valid, __LINE__); \
|
||||
}; \
|
||||
;
|
||||
|
||||
#define rassert(expr) \
|
||||
{ \
|
||||
int __valid = expr ? 1 : 0; \
|
||||
rtest_test_true(#expr, __valid, __LINE__); \
|
||||
}; \
|
||||
;
|
||||
#define rtest_asserts(expr) \
|
||||
{ \
|
||||
int __valid = expr ? 1 : 0; \
|
||||
rtest_test_true_silent(#expr, __valid, __LINE__); \
|
||||
};
|
||||
#define rasserts(expr) \
|
||||
{ \
|
||||
int __valid = expr ? 1 : 0; \
|
||||
rtest_test_true_silent(#expr, __valid, __LINE__); \
|
||||
};
|
||||
#define rtest_false(expr) \
|
||||
rprintf(" [%s]\t%s\t\n", expr == 0 ? "OK" : "NOK", #expr); \
|
||||
assert_count++; \
|
||||
assert(#expr);
|
||||
#define rtest_skip(expr) rtest_test_skip(#expr, __LINE__);
|
||||
|
||||
FILE *rtest_create_file(char *path, char *content) {
|
||||
FILE *fd = fopen(path, "wb");
|
||||
|
||||
char c;
|
||||
int index = 0;
|
||||
|
||||
while ((c = content[index]) != 0) {
|
||||
fputc(c, fd);
|
||||
index++;
|
||||
}
|
||||
fclose(fd);
|
||||
fd = fopen(path, "rb");
|
||||
return fd;
|
||||
}
|
||||
|
||||
void rtest_delete_file(char *path) { unlink(path); }
|
||||
#endif
|
19
rtime.c
Normal file
19
rtime.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include "rtime.h"
|
||||
#include "rtest.h"
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
rtest_banner("time");
|
||||
rtest_banner("Milliseconds tests");
|
||||
rtest_assert(!strcmp(msecs_str(0), "0Ms"));
|
||||
rtest_assert(!strcmp(msecs_str(1), "1Ms"));
|
||||
rtest_assert(!strcmp(msecs_str(12), "12Ms"));
|
||||
rtest_assert(!strcmp(msecs_str(123), "123Ms"));
|
||||
rtest_assert(!strcmp(msecs_str(999), "999Ms"));
|
||||
rtest_banner("Second tests");
|
||||
rtest_assert(!strcmp(msecs_str(1000), "1s"));
|
||||
rtest_assert(!strcmp(msecs_str(1100), "1.1s"));
|
||||
rtest_assert(!strcmp(msecs_str(1234), "1.234s"));
|
||||
rtest_assert(!strcmp(msecs_str(12345), "12.345s"));
|
||||
return rtest_end("successs");
|
||||
}
|
147
rtime.h
Normal file
147
rtime.h
Normal file
@ -0,0 +1,147 @@
|
||||
|
||||
#ifndef RLIB_TIME
|
||||
#define RLIB_TIME
|
||||
|
||||
#ifndef _POSIX_C_SOURCE_199309L
|
||||
|
||||
#define _POSIX_C_SOURCE_199309L
|
||||
#endif
|
||||
#include "rtemp.h"
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#undef _POSIX_C_SOURCE_199309L
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifndef CLOCK_MONOTONIC
|
||||
#define CLOCK_MONOTONIC 1
|
||||
#endif
|
||||
|
||||
typedef uint64_t nsecs_t;
|
||||
void nsleep(nsecs_t nanoseconds);
|
||||
|
||||
void tick() { nsleep(1); }
|
||||
|
||||
typedef unsigned long long msecs_t;
|
||||
|
||||
nsecs_t nsecs() {
|
||||
unsigned int lo, hi;
|
||||
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
}
|
||||
|
||||
msecs_t rnsecs_to_msecs(nsecs_t nsecs) { return nsecs / 1000 / 1000; }
|
||||
|
||||
nsecs_t rmsecs_to_nsecs(msecs_t msecs) { return msecs * 1000 * 1000; }
|
||||
|
||||
msecs_t usecs() {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (long long)(tv.tv_sec) * 1000000 + (long long)(tv.tv_usec);
|
||||
}
|
||||
|
||||
msecs_t msecs() {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (long long)(tv.tv_sec) * 1000 + (tv.tv_usec / 1000);
|
||||
}
|
||||
char *msecs_strs(msecs_t ms) {
|
||||
static char str[22];
|
||||
str[0] = 0;
|
||||
sprintf(str, "%f", ms * 0.001);
|
||||
for (int i = strlen(str); i > 0; i--) {
|
||||
if (str[i] > '0')
|
||||
break;
|
||||
str[i] = 0;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
char *msecs_strms(msecs_t ms) {
|
||||
static char str[22];
|
||||
str[0] = 0;
|
||||
sprintf(str, "%lld", ms);
|
||||
return str;
|
||||
}
|
||||
char *msecs_str(long long ms) {
|
||||
static char result[30];
|
||||
result[0] = 0;
|
||||
if (ms > 999) {
|
||||
char *s = msecs_strs(ms);
|
||||
sprintf(result, "%ss", s);
|
||||
} else {
|
||||
char *s = msecs_strms(ms);
|
||||
sprintf(result, "%sMs", s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nsleep(nsecs_t nanoseconds) {
|
||||
long seconds = 0;
|
||||
int factor = 0;
|
||||
while (nanoseconds > 1000000000) {
|
||||
factor++;
|
||||
nanoseconds = nanoseconds / 10;
|
||||
}
|
||||
if (factor) {
|
||||
seconds = 1;
|
||||
factor--;
|
||||
while (factor) {
|
||||
seconds = seconds * 10;
|
||||
factor--;
|
||||
}
|
||||
}
|
||||
|
||||
struct timespec req = {seconds, nanoseconds};
|
||||
struct timespec rem;
|
||||
|
||||
nanosleep(&req, &rem);
|
||||
}
|
||||
|
||||
void ssleep(double s) {
|
||||
long nanoseconds = (long)(1000000000 * s);
|
||||
|
||||
// long seconds = 0;
|
||||
|
||||
// struct timespec req = {seconds, nanoseconds};
|
||||
// struct timespec rem;
|
||||
|
||||
nsleep(nanoseconds);
|
||||
}
|
||||
void msleep(long miliseonds) {
|
||||
long nanoseconds = miliseonds * 1000000;
|
||||
nsleep(nanoseconds);
|
||||
}
|
||||
|
||||
char *format_time(int64_t nanoseconds) {
|
||||
char output[1024];
|
||||
size_t output_size = sizeof(output);
|
||||
output[0] = 0;
|
||||
if (nanoseconds < 1000) {
|
||||
// Less than 1 microsecond
|
||||
snprintf(output, output_size, "%ldns", nanoseconds);
|
||||
} else if (nanoseconds < 1000000) {
|
||||
// Less than 1 millisecond
|
||||
double us = nanoseconds / 1000.0;
|
||||
snprintf(output, output_size, "%.2fµs", us);
|
||||
} else if (nanoseconds < 1000000000) {
|
||||
// Less than 1 second
|
||||
double ms = nanoseconds / 1000000.0;
|
||||
snprintf(output, output_size, "%.2fms", ms);
|
||||
} else {
|
||||
// 1 second or more
|
||||
double s = nanoseconds / 1000000000.0;
|
||||
if (s > 60 * 60) {
|
||||
s = s / 60 / 60;
|
||||
snprintf(output, output_size, "%.2fh", s);
|
||||
} else if (s > 60) {
|
||||
s = s / 60;
|
||||
snprintf(output, output_size, "%.2fm", s);
|
||||
} else {
|
||||
snprintf(output, output_size, "%.2fs", s);
|
||||
}
|
||||
}
|
||||
return sbuf(output);
|
||||
}
|
||||
|
||||
#endif
|
67
rtree.c
Normal file
67
rtree.c
Normal file
@ -0,0 +1,67 @@
|
||||
#include "rtree.h"
|
||||
#include "rbench.h"
|
||||
#include "rtest.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct rtree_bench_data_t {
|
||||
rtree_t *rtree;
|
||||
char *value;
|
||||
} rtree_bench_data_t;
|
||||
|
||||
void rtree_bench_f() {
|
||||
rtree_bench_data_t *bd = rbf->data;
|
||||
if (rbf->first) {
|
||||
static rtree_bench_data_t bds;
|
||||
bds.rtree = rtree_new();
|
||||
bds.value = strdup(rgenerate_key());
|
||||
rbf->data = &bds;
|
||||
bd = rbf->data;
|
||||
}
|
||||
char *key = rgenerate_key();
|
||||
rtree_set(bd->rtree, key, bd->value);
|
||||
char *value = (char *)rtree_get(bd->rtree, key);
|
||||
rassert(!strcmp(value, bd->value));
|
||||
if (rbf->last) {
|
||||
free(bd->value);
|
||||
rtree_free(bd->rtree);
|
||||
}
|
||||
}
|
||||
|
||||
void rtree_bench(long item_count) {
|
||||
rbench_t *b = rbench_new();
|
||||
b->stdout = false;
|
||||
b->silent = true;
|
||||
b->add_function(b, "random_key", "rtree_rw", rtree_bench_f);
|
||||
b->execute(b, 1000000);
|
||||
// rassert(rnsecs_to_msecs(b->execution_time) < 700); // faster than 700ms
|
||||
printf("r/w %ld items and deallocated in %s\n", item_count, format_time(b->execution_time));
|
||||
rbench_free(b);
|
||||
}
|
||||
|
||||
int main() {
|
||||
rtest_banner("rtree");
|
||||
rtree_t *rtree = rtree_new();
|
||||
// Test new object default valuess
|
||||
rtest_banner("New object default values");
|
||||
rtest_assert(rtree->c == 0);
|
||||
rtest_assert(rtree->next == NULL);
|
||||
rtest_assert(rtree->children == NULL);
|
||||
rtest_assert(rtree->data == NULL);
|
||||
// Test set
|
||||
rtest_banner("Set");
|
||||
rtree_set(rtree, "a", "data");
|
||||
rtest_assert(rtree->c == 'a');
|
||||
rtest_assert(!strcmp(rtree->data, "data"));
|
||||
// Second element should be filled
|
||||
rtree_set(rtree, "b", "data2");
|
||||
rtest_assert(rtree->next->c == 'b');
|
||||
rtest_assert(!strcmp(rtree->next->data, "data2"));
|
||||
// First child of second element should be filled
|
||||
rtree_set(rtree, "bc", "data3");
|
||||
rtest_assert(rtree->next->children->c == 'c');
|
||||
rtest_assert(!strcmp(rtree->next->children->data, "data3"));
|
||||
rtree_free(rtree);
|
||||
rtest_banner("Benchmark");
|
||||
rtree_bench(1000000);
|
||||
return rtest_end("");
|
||||
}
|
95
rtree.h
Normal file
95
rtree.h
Normal file
@ -0,0 +1,95 @@
|
||||
#ifndef RTREE_H
|
||||
#define RTREE_H
|
||||
#include "rmalloc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct rtree_t {
|
||||
struct rtree_t *next;
|
||||
struct rtree_t *children;
|
||||
char c;
|
||||
void *data;
|
||||
} rtree_t;
|
||||
|
||||
rtree_t *rtree_new() {
|
||||
rtree_t *b = (rtree_t *)rmalloc(sizeof(rtree_t));
|
||||
b->next = NULL;
|
||||
b->children = NULL;
|
||||
b->c = 0;
|
||||
b->data = NULL;
|
||||
return b;
|
||||
}
|
||||
|
||||
rtree_t *rtree_set(rtree_t *b, char *c, void *data) {
|
||||
while (b) {
|
||||
if (b->c == 0) {
|
||||
b->c = *c;
|
||||
c++;
|
||||
if (*c == 0) {
|
||||
b->data = data;
|
||||
// printf("SET1 %c\n", b->c);
|
||||
return b;
|
||||
}
|
||||
} else if (b->c == *c) {
|
||||
c++;
|
||||
if (*c == 0) {
|
||||
b->data = data;
|
||||
return b;
|
||||
}
|
||||
if (b->children) {
|
||||
b = b->children;
|
||||
} else {
|
||||
b->children = rtree_new();
|
||||
b = b->children;
|
||||
}
|
||||
} else if (b->next) {
|
||||
b = b->next;
|
||||
} else {
|
||||
b->next = rtree_new();
|
||||
b = b->next;
|
||||
b->c = *c;
|
||||
c++;
|
||||
if (*c == 0) {
|
||||
b->data = data;
|
||||
return b;
|
||||
} else {
|
||||
b->children = rtree_new();
|
||||
b = b->children;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rtree_t *rtree_find(rtree_t *b, char *c) {
|
||||
while (b) {
|
||||
if (b->c == *c) {
|
||||
c++;
|
||||
if (*c == 0) {
|
||||
return b;
|
||||
}
|
||||
b = b->children;
|
||||
continue;
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rtree_free(rtree_t *b) {
|
||||
if (!b)
|
||||
return;
|
||||
rtree_free(b->children);
|
||||
rtree_free(b->next);
|
||||
rfree(b);
|
||||
}
|
||||
|
||||
void *rtree_get(rtree_t *b, char *c) {
|
||||
rtree_t *t = rtree_find(b, c);
|
||||
if (t) {
|
||||
return t->data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
14
rtypes.c
Normal file
14
rtypes.c
Normal file
@ -0,0 +1,14 @@
|
||||
#include "rtest.h"
|
||||
#include "rtypes.h"
|
||||
|
||||
int main() {
|
||||
rtest_banner("rtypes");
|
||||
uint test1 = 1000;
|
||||
rassert(test1 == 1000) ulong test2 = -1000;
|
||||
rassert(test2 == -1000);
|
||||
byte test3 = 123;
|
||||
rassert(test3 == 123);
|
||||
byte test4 = 'A';
|
||||
rassert(test4 == 65);
|
||||
return rtest_end("");
|
||||
}
|
32
rtypes.h
Normal file
32
rtypes.h
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
#ifndef RTYPES_H
|
||||
#define RTYPES_H
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE_TEMP _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#ifndef _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h> // uint
|
||||
#include <sys/types.h> // ulong
|
||||
#include <string.h>
|
||||
#ifndef ulonglong
|
||||
#define ulonglong unsigned long long
|
||||
#endif
|
||||
#ifndef uint
|
||||
typedef unsigned int uint;
|
||||
#endif
|
||||
#ifndef byte
|
||||
typedef unsigned char byte;
|
||||
#endif
|
||||
#ifdef _POSIX_C_SOURCE_TEMP
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE _POSIX_C_SOURCE_TEMP
|
||||
#undef _POSIX_C_SOURCE_TEMP
|
||||
#else
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
#endif
|
46
uuid.h
Normal file
46
uuid.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef UUID_H
|
||||
#define UUID_H
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
#include "rtemp.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned char bytes[16];
|
||||
} UUID;
|
||||
|
||||
void generate_random_bytes(unsigned char *bytes, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
bytes[i] = rand() % 256;
|
||||
}
|
||||
}
|
||||
|
||||
UUID generate_uuid4(void) {
|
||||
UUID uuid;
|
||||
|
||||
generate_random_bytes(uuid.bytes, 16);
|
||||
|
||||
uuid.bytes[6] &= 0x0f;
|
||||
uuid.bytes[6] |= 0x40;
|
||||
|
||||
uuid.bytes[8] &= 0x3f;
|
||||
uuid.bytes[8] |= 0x80;
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
void uuid_to_string(UUID uuid, char *str) {
|
||||
sprintf(str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.bytes[0], uuid.bytes[1], uuid.bytes[2],
|
||||
uuid.bytes[3], uuid.bytes[4], uuid.bytes[5], uuid.bytes[6], uuid.bytes[7], uuid.bytes[8], uuid.bytes[9], uuid.bytes[10],
|
||||
uuid.bytes[11], uuid.bytes[12], uuid.bytes[13], uuid.bytes[14], uuid.bytes[15]);
|
||||
}
|
||||
|
||||
char *uuid4() {
|
||||
srand(time(NULL));
|
||||
UUID uuid = generate_uuid4();
|
||||
char str[37];
|
||||
uuid_to_string(uuid, str);
|
||||
return sbuf(str);
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user