Skip to content

Commit ded98bd

Browse files
anonriglemire
andauthored
feat: add c bindings (#372)
* feat: add C bindings * some fixes to the C API (#375) * fix linting errors * add ada_c.h to release script --------- Co-authored-by: Daniel Lemire <daniel@lemire.me>
1 parent 4e5b900 commit ded98bd

File tree

15 files changed

+658
-6
lines changed

15 files changed

+658
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ docs/theme
1616
benchmark_result.json
1717

1818
singleheader/ada.h
19+
singleheader/ada_c.h
1920
singleheader/ada.cpp
2021
singleheader/singleheader.zip
2122

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ if(NOT ADA_COVERAGE)
7575
endif()
7676

7777
install(
78-
FILES include/ada.h
78+
FILES include/ada.h include/ada_c.h
7979
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
8080
COMPONENT ada_development
8181
)

README.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Ada is a fast and spec-compliant URL parser written in C++.
99
Specification for URL parser can be found from the
1010
[WHATWG](https://url.spec.whatwg.org/#url-parsing) website.
1111

12+
We also include a C wrapper for portability.
13+
1214
The Ada library passes the full range of tests from the specification,
1315
across a wide range of platforms (e.g., Windows, Linux, macOS). It fully
1416
supports the relevant [Unicode Technical Standard](https://www.unicode.org/reports/tr46/#ToUnicode).
@@ -122,6 +124,53 @@ url->set_hash("is-this-the-real-life");
122124
// url->get_hash() will return "#is-this-the-real-life"
123125
```
124126
127+
### C wrapper
128+
129+
See the file `include/ada_c.h` for our C interface.
130+
131+
```C
132+
#include "ada_c.h"
133+
#include <stdio.h>
134+
#include <stdlib.h>
135+
#include <stdbool.h>
136+
137+
static void ada_print(ada_string string) {
138+
printf("%.*s\n", (int)string.length, string.data);
139+
}
140+
141+
int main(int c, char *arg[] ) {
142+
ada_url url = ada_parse("https://username:password@www.google.com:8080/"
143+
"pathname?query=true#hash-exists");
144+
if(!ada_is_valid(url)) { puts("failure"); return EXIT_FAILURE; }
145+
ada_print(ada_get_href(url)); // prints https://username:password@host:8080/pathname?query=true#hash-exists
146+
ada_print(ada_get_protocol(url)); // prints https:
147+
ada_print(ada_get_username(url)); // prints username
148+
ada_set_href(url, "https://www.yagiz.co");
149+
if(!ada_is_valid(url)) { puts("failure"); return EXIT_FAILURE; }
150+
ada_set_hash(url, "new-hash");
151+
ada_set_hostname(url, "new-host");
152+
ada_set_host(url, "changed-host:9090");
153+
ada_set_pathname(url, "new-pathname");
154+
ada_set_search(url, "new-search");
155+
ada_set_protocol(url, "wss");
156+
ada_print(ada_get_href(url)); // will print wss://changed-host:9090/new-pathname?new-search#new-hash
157+
ada_free(url);
158+
return EXIT_SUCCESS;
159+
}
160+
```
161+
162+
When linking against the ada library from C++, be minding that ada requires access to the standard
163+
C++ library. E.g., you may link with the C++ compiler.
164+
165+
E.g., if you grab our single-header C++ files (`ada.cpp` and `ada.h`), as well as the C header (`ada_c.h`),
166+
you can often compile a C program (`demo.c`) as follows under Linux/macOS systems:
167+
168+
```
169+
c++ -c ada.cpp -std=c++17
170+
cc -c demo.c
171+
c++ demo.o ada.o -o cdemo
172+
./cdemo
173+
```
125174

126175
### CMake dependency
127176

@@ -159,5 +208,4 @@ You may amalgamate all source files into only two files (`ada.h` and `ada.cpp`)
159208

160209
This code is made available under the Apache License 2.0 as well as the MIT license.
161210

162-
Our tests include third-party code and data. The benchmarking code includes third-party code:
163-
it is provided for research purposes only and not part of the library.
211+
Our tests include third-party code and data. The benchmarking code includes third-party code: it is provided for research purposes only and not part of the library.

include/ada_c.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @file ada_c.h
3+
* @brief Includes the C definitions for Ada. This is a C file, not C++.
4+
*/
5+
#ifndef ADA_C_H
6+
#define ADA_C_H
7+
8+
#include <stdbool.h>
9+
#include <stdint.h>
10+
#include <stddef.h>
11+
12+
// string that is owned by the ada_url instance
13+
typedef struct {
14+
const char* data;
15+
size_t length;
16+
} ada_string;
17+
18+
// string that must be freed by the caller
19+
typedef struct {
20+
const char* data;
21+
size_t length;
22+
} ada_owned_string;
23+
24+
typedef struct {
25+
uint32_t protocol_end;
26+
uint32_t username_end;
27+
uint32_t host_start;
28+
uint32_t host_end;
29+
uint32_t port;
30+
uint32_t pathname_start;
31+
uint32_t search_start;
32+
uint32_t hash_start;
33+
} ada_url_components;
34+
35+
typedef void* ada_url;
36+
37+
// input should be a null terminated C string
38+
// you must call ada_free on the returned pointer
39+
ada_url ada_parse(const char* string);
40+
41+
// input and base should be a null terminated C strings
42+
bool ada_can_parse(const char* input, const char* base);
43+
44+
void ada_free(ada_url result);
45+
46+
bool ada_is_valid(ada_url result);
47+
48+
// url_aggregator getters
49+
// if ada_is_valid(result)) is false, an empty string is returned
50+
ada_owned_string ada_get_origin(ada_url result);
51+
void ada_free_owned_string(ada_owned_string owned);
52+
ada_string ada_get_href(ada_url result);
53+
ada_string ada_get_username(ada_url result);
54+
ada_string ada_get_password(ada_url result);
55+
ada_string ada_get_port(ada_url result);
56+
ada_string ada_get_hash(ada_url result);
57+
ada_string ada_get_host(ada_url result);
58+
ada_string ada_get_hostname(ada_url result);
59+
ada_string ada_get_pathname(ada_url result);
60+
ada_string ada_get_search(ada_url result);
61+
ada_string ada_get_protocol(ada_url result);
62+
63+
// url_aggregator setters
64+
// if ada_is_valid(result)) is false, the setters have no effect
65+
// input should be a null terminated C string
66+
bool ada_set_href(ada_url result, const char* input);
67+
bool ada_set_host(ada_url result, const char* input);
68+
bool ada_set_hostname(ada_url result, const char* input);
69+
bool ada_set_protocol(ada_url result, const char* input);
70+
bool ada_set_username(ada_url result, const char* input);
71+
bool ada_set_password(ada_url result, const char* input);
72+
bool ada_set_port(ada_url result, const char* input);
73+
bool ada_set_pathname(ada_url result, const char* input);
74+
void ada_set_search(ada_url result, const char* input);
75+
void ada_set_hash(ada_url result, const char* input);
76+
77+
// url_aggregator functions
78+
// if ada_is_valid(result) is false, functions below will return false
79+
bool ada_has_credentials(ada_url result);
80+
bool ada_has_empty_hostname(ada_url result);
81+
bool ada_has_hostname(ada_url result);
82+
bool ada_has_non_empty_username(ada_url result);
83+
bool ada_has_non_empty_password(ada_url result);
84+
bool ada_has_port(ada_url result);
85+
bool ada_has_password(ada_url result);
86+
bool ada_has_hash(ada_url result);
87+
bool ada_has_search(ada_url result);
88+
89+
// returns a pointer to the internal url_aggregator::url_components
90+
const ada_url_components* ada_get_components(ada_url result);
91+
92+
#endif // ADA_C_H

singleheader/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
set(SINGLEHEADER_FILES
55
${CMAKE_CURRENT_BINARY_DIR}/ada.cpp
66
${CMAKE_CURRENT_BINARY_DIR}/ada.h
7+
${CMAKE_CURRENT_BINARY_DIR}/ada_c.h
78
${CMAKE_CURRENT_BINARY_DIR}/demo.cpp
9+
${CMAKE_CURRENT_BINARY_DIR}/demo.c
810
${CMAKE_CURRENT_BINARY_DIR}/README.md
911
)
1012
set_source_files_properties(${SINGLEHEADER_FILES} PROPERTIES GENERATED TRUE)
@@ -52,6 +54,11 @@ if (Python3_Interpreter_FOUND)
5254
target_link_libraries(demo ada-singleheader-include-source)
5355

5456
add_test(demo demo)
57+
58+
add_executable(cdemo $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/demo.c>)
59+
target_link_libraries(cdemo ada)
60+
61+
add_test(cdemo cdemo)
5562
endif()
5663
else()
5764
MESSAGE( STATUS "Python not found, we are unable to test amalgamate.py." )

singleheader/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,15 @@ c++ -std=c++17 -o demo demo.cpp
2222
```
2323

2424
You may build and link using CMake (--target demo), because CMake can configure all the necessary flags.
25+
26+
27+
### C Demo
28+
29+
You may also build a C executable.
30+
31+
```
32+
c++ -c ada.cpp -std=c++17
33+
cc -c demo.c
34+
c++ demo.o ada.o -o cdemo
35+
./cdemo
36+
```

singleheader/amalgamate.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,15 @@ def dofile(fid: str, prepath: str, filename: str) -> None:
128128
# copy the README and DEMOCPP
129129
if SCRIPTPATH != AMALGAMATE_OUTPUT_PATH:
130130
shutil.copy2(os.path.join(SCRIPTPATH,"demo.cpp"),AMALGAMATE_OUTPUT_PATH)
131+
shutil.copy2(os.path.join(SCRIPTPATH,"demo.c"),AMALGAMATE_OUTPUT_PATH)
131132
shutil.copy2(os.path.join(SCRIPTPATH,"README.md"),AMALGAMATE_OUTPUT_PATH)
132133

134+
shutil.copy2(os.path.join(AMALGAMATE_INCLUDE_PATH,"ada_c.h"),AMALGAMATE_OUTPUT_PATH)
135+
133136
zf = zipfile.ZipFile(os.path.join(AMALGAMATE_OUTPUT_PATH,'singleheader.zip'), 'w', zipfile.ZIP_DEFLATED)
134137
zf.write(os.path.join(AMALGAMATE_OUTPUT_PATH,"ada.cpp"), "ada.cpp")
135138
zf.write(os.path.join(AMALGAMATE_OUTPUT_PATH,"ada.h"), "ada.h")
139+
zf.write(os.path.join(AMALGAMATE_INCLUDE_PATH,"ada_c.h"), "ada_c.h")
136140

137141

138142
print("Done with all files generation.")

singleheader/demo.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include "ada_c.h"
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <stdbool.h>
5+
6+
static void ada_print(ada_string string) {
7+
printf("%.*s\n", (int)string.length, string.data);
8+
}
9+
10+
int main(int c, char *arg[]) {
11+
ada_url url = ada_parse(
12+
"https://username:password@www.google.com:8080/"
13+
"pathname?query=true#hash-exists");
14+
if (!ada_is_valid(url)) {
15+
puts("failure");
16+
return EXIT_FAILURE;
17+
}
18+
ada_print(ada_get_href(
19+
url)); // prints
20+
// https://username:password@host:8080/pathname?query=true#hash-exists
21+
ada_print(ada_get_protocol(url)); // prints https:
22+
ada_print(ada_get_username(url)); // prints username
23+
ada_set_href(url, "https://www.yagiz.co");
24+
if (!ada_is_valid(url)) {
25+
puts("failure");
26+
return EXIT_FAILURE;
27+
}
28+
ada_set_hash(url, "new-hash");
29+
ada_set_hostname(url, "new-host");
30+
ada_set_host(url, "changed-host:9090");
31+
ada_set_pathname(url, "new-pathname");
32+
ada_set_search(url, "new-search");
33+
ada_set_protocol(url, "wss");
34+
ada_print(ada_get_href(
35+
url)); // will print
36+
// wss://changed-host:9090/new-pathname?new-search#new-hash
37+
ada_free(url);
38+
return EXIT_SUCCESS;
39+
}

src/ada.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
#include "url-setters.cpp"
1010
#include "parser.cpp"
1111
#include "url_components.cpp"
12-
#include "url_aggregator.cpp"
12+
#include "url_aggregator.cpp"
13+
#include "ada_c.cpp"

0 commit comments

Comments
 (0)