mirror of
https://github.com/Wessel/c-websocket-server.git
synced 2026-07-14 20:44:00 +02:00
feat: Populate repo
This commit is contained in:
148
lib/array.c
Normal file
148
lib/array.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/* array.c */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/** @brief Prints the values of `arr` into `STDOUT`
|
||||
*
|
||||
* C does not usually come with a way to print arrays,
|
||||
* this function makes that possible.
|
||||
*
|
||||
* @param arr The array to print
|
||||
* @param size The size of `arr`
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
void print_array(int arr[], int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
printf("Array[%d]\t= %d\n", i, arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Find the key `index` of `needle` in `arr`
|
||||
*
|
||||
* In order to find a value's place in an array, we can
|
||||
* use the following function to find `needle` in `arr`.
|
||||
*
|
||||
* @param arr The array to look through
|
||||
* @param size The size of `arr`
|
||||
* @param needle The value to search for
|
||||
*
|
||||
* @returns index The index of `needle` if found, else `-1`
|
||||
*/
|
||||
int find_index(int arr[], int size, int needle) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (arr[i] == needle) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** @brief Find the highest three numbers in `arr`
|
||||
*
|
||||
* In order to find the highest three values in an array,
|
||||
* a loop and a set of if statements can be used.
|
||||
*
|
||||
* @param arr The array to look through
|
||||
* @param size The size of `arr`
|
||||
*
|
||||
* @returns max An array with the highest three numbers in `arr`
|
||||
*/
|
||||
int* largest_three(int arr[], int size) {
|
||||
int* max = malloc(3);
|
||||
max[0] = arr[0];
|
||||
max[1] = arr[1];
|
||||
max[2] = arr[2];
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (arr[i] > max[0]) {
|
||||
max[2] = max[1];
|
||||
max[1] = max[0];
|
||||
max[0] = arr[i];
|
||||
}
|
||||
else if (arr[i] > max[1]) {
|
||||
max[2] = max[1];
|
||||
max[1] = arr[i];
|
||||
}
|
||||
else if (arr[i] > max[2]) {
|
||||
max[2] = arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/** @brief Inverse the contents of `arr`
|
||||
*
|
||||
* This function will return the inversed version of `arr`.
|
||||
* This will mean that arr[last] = inversed_array[first].
|
||||
*
|
||||
* @param arr The array to inverse
|
||||
* @param size The size of `arr`
|
||||
*
|
||||
* @returns inversed_array An inversed version of `arr`
|
||||
*/
|
||||
int* inverse_array(int arr[], int size) {
|
||||
int* inversed_array = malloc(size);
|
||||
|
||||
for (int i = size; i > 0; i--) {
|
||||
inversed_array[size - i] = arr[i - 1];
|
||||
}
|
||||
|
||||
return inversed_array;
|
||||
}
|
||||
|
||||
/** @brief Sorts `arr` alphabetically using bubble sorting
|
||||
*
|
||||
* In order to alphabetically sort `arr` filled with `char`,
|
||||
* the bubble sorting algorithm can be used.
|
||||
* (THIS FUNCTION OVERWRITES `arr`.)
|
||||
*
|
||||
* @param arr The array to sort
|
||||
* @param size The size of `arr`
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
void sort_char_array(char arr[], int size) {
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
for (int j = 0; j < size - i -1; j++) {
|
||||
if (arr[j] > arr[j + 1]) {
|
||||
char temp = arr[j];
|
||||
arr[j] = arr[j + 1];
|
||||
arr[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Filters out any non-lowercase characters from `arr`,
|
||||
* then passes it to `sort_char_array()`
|
||||
*
|
||||
* In order to filter out any non-lowercase characters and then
|
||||
* sort the array alphabetically, the function below is used.
|
||||
* It will loop through the array and check if the charcodes are inbetween
|
||||
* 'a' and 'z', if not removes them.
|
||||
*
|
||||
* @param arr The array to filter and sort
|
||||
* @param size The size of `arr`
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
void filter_lowercase_and_sort(char arr[], int size) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (arr[i] >= 'a' && arr[i] <= 'z') {
|
||||
arr[count++] = arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
sort_char_array(arr, count);
|
||||
}
|
||||
|
||||
// void print_array(void *arr, int size, size_t elem_size, char *format) {
|
||||
// for (int i = 0; i < size; i++) {
|
||||
// printf("Array[%d] = ", i);
|
||||
// char *elem = (char *) arr + i * elem_size;
|
||||
// printf(format, *(int *) elem);
|
||||
// printf("\n");
|
||||
// }
|
||||
// }
|
||||
155
lib/base64.c
Normal file
155
lib/base64.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Base64 encoding/decoding (RFC1341)
|
||||
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <base64.h>
|
||||
|
||||
static const unsigned char base64_table[65] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/**
|
||||
* base64_encode - Base64 encode
|
||||
* @src: Data to be encoded
|
||||
* @len: Length of the data to be encoded
|
||||
* @out_len: Pointer to output length variable, or %NULL if not used
|
||||
* Returns: Allocated buffer of out_len bytes of encoded data,
|
||||
* or %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer. Returned buffer is
|
||||
* nul terminated to make it easier to use as a C string. The nul terminator is
|
||||
* not included in out_len.
|
||||
*/
|
||||
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len)
|
||||
{
|
||||
unsigned char *out, *pos;
|
||||
const unsigned char *end, *in;
|
||||
size_t olen;
|
||||
int line_len;
|
||||
|
||||
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||||
olen += olen / 72; /* line feeds */
|
||||
olen++; /* nul termination */
|
||||
if (olen < len)
|
||||
return NULL; /* integer overflow */
|
||||
out = malloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
end = src + len;
|
||||
in = src;
|
||||
pos = out;
|
||||
line_len = 0;
|
||||
while (end - in >= 3) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||||
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||||
*pos++ = base64_table[in[2] & 0x3f];
|
||||
in += 3;
|
||||
line_len += 4;
|
||||
if (line_len >= 72) {
|
||||
*pos++ = '\n';
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (end - in) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
if (end - in == 1) {
|
||||
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||||
*pos++ = '=';
|
||||
} else {
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||||
(in[1] >> 4)];
|
||||
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||||
}
|
||||
*pos++ = '=';
|
||||
line_len += 4;
|
||||
}
|
||||
|
||||
if (line_len)
|
||||
*pos++ = '\n';
|
||||
|
||||
*pos = '\0';
|
||||
if (out_len)
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* base64_decode - Base64 decode
|
||||
* @src: Data to be decoded
|
||||
* @len: Length of the data to be decoded
|
||||
* @out_len: Pointer to output length variable
|
||||
* Returns: Allocated buffer of out_len bytes of decoded data,
|
||||
* or %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer.
|
||||
*/
|
||||
unsigned char * base64_decode(const unsigned char *src, size_t len,
|
||||
size_t *out_len)
|
||||
{
|
||||
unsigned char dtable[256], *out, *pos, block[4], tmp;
|
||||
size_t i, count, olen;
|
||||
int pad = 0;
|
||||
|
||||
memset(dtable, 0x80, 256);
|
||||
for (i = 0; i < sizeof(base64_table) - 1; i++)
|
||||
dtable[base64_table[i]] = (unsigned char) i;
|
||||
dtable['='] = 0;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (dtable[src[i]] != 0x80)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0 || count % 4)
|
||||
return NULL;
|
||||
|
||||
olen = count / 4 * 3;
|
||||
pos = out = malloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = dtable[src[i]];
|
||||
if (tmp == 0x80)
|
||||
continue;
|
||||
|
||||
if (src[i] == '=')
|
||||
pad++;
|
||||
block[count] = tmp;
|
||||
count++;
|
||||
if (count == 4) {
|
||||
*pos++ = (block[0] << 2) | (block[1] >> 4);
|
||||
*pos++ = (block[1] << 4) | (block[2] >> 2);
|
||||
*pos++ = (block[2] << 6) | block[3];
|
||||
count = 0;
|
||||
if (pad) {
|
||||
if (pad == 1)
|
||||
pos--;
|
||||
else if (pad == 2)
|
||||
pos -= 2;
|
||||
else {
|
||||
/* Invalid padding */
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
124
lib/handshake.c
Normal file
124
lib/handshake.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2020 Davidson Francis <davidsondfgl@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <base64.h>
|
||||
#include <sha1.h>
|
||||
#include <ws.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @dir src/
|
||||
* @brief Handshake routines directory
|
||||
*
|
||||
* @file handshake.c
|
||||
* @brief Handshake routines.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Gets the field Sec-WebSocket-Accept on response, by
|
||||
* an previously informed key.
|
||||
*
|
||||
* @param wsKey Sec-WebSocket-Key
|
||||
* @param dest source to be stored the value.
|
||||
*
|
||||
* @return Returns 0 if success and a negative number
|
||||
* otherwise.
|
||||
*
|
||||
* @attention This is part of the internal API and is documented just
|
||||
* for completeness.
|
||||
*/
|
||||
int get_handshake_accept(char *wsKey, unsigned char **dest)
|
||||
{
|
||||
unsigned char hash[SHA1HashSize]; /* SHA-1 Hash. */
|
||||
SHA1Context ctx; /* SHA-1 Context. */
|
||||
char *str; /* WebSocket key + magic string. */
|
||||
|
||||
/* Invalid key. */
|
||||
if (!wsKey)
|
||||
return (-1);
|
||||
|
||||
str = calloc(1, sizeof(char) * (WS_KEY_LEN + WS_MS_LEN + 1));
|
||||
if (!str)
|
||||
return (-1);
|
||||
|
||||
strncpy(str, wsKey, WS_KEY_LEN);
|
||||
strcat(str, MAGIC_STRING);
|
||||
|
||||
SHA1Reset(&ctx);
|
||||
SHA1Input(&ctx, (const uint8_t *)str, WS_KEYMS_LEN);
|
||||
SHA1Result(&ctx, hash);
|
||||
|
||||
*dest = base64_encode(hash, SHA1HashSize, NULL);
|
||||
*(*dest + strlen((const char *)*dest) - 1) = '\0';
|
||||
free(str);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the complete response to accomplish a succesfully
|
||||
* handshake.
|
||||
*
|
||||
* @param hsrequest Client request.
|
||||
* @param hsresponse Server response.
|
||||
*
|
||||
* @return Returns 0 if success and a negative number
|
||||
* otherwise.
|
||||
*
|
||||
* @attention This is part of the internal API and is documented just
|
||||
* for completeness.
|
||||
*/
|
||||
int get_handshake_response(char *hsrequest, char **hsresponse)
|
||||
{
|
||||
unsigned char *accept; /* Accept message. */
|
||||
char *saveptr; /* strtok_r() pointer. */
|
||||
char *s; /* Current string. */
|
||||
int ret; /* Return value. */
|
||||
|
||||
saveptr = NULL;
|
||||
for (s = strtok_r(hsrequest, "\r\n", &saveptr); s != NULL;
|
||||
s = strtok_r(NULL, "\r\n", &saveptr))
|
||||
{
|
||||
if (strstr(s, WS_HS_REQ) != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Ensure that we have a valid pointer. */
|
||||
if (s == NULL)
|
||||
return (-1);
|
||||
|
||||
saveptr = NULL;
|
||||
s = strtok_r(s, " ", &saveptr);
|
||||
s = strtok_r(NULL, " ", &saveptr);
|
||||
|
||||
ret = get_handshake_accept(s, &accept);
|
||||
if (ret < 0)
|
||||
return (ret);
|
||||
|
||||
*hsresponse = malloc(sizeof(char) * WS_HS_ACCLEN);
|
||||
if (*hsresponse == NULL)
|
||||
return (-1);
|
||||
|
||||
strcpy(*hsresponse, WS_HS_ACCEPT);
|
||||
strcat(*hsresponse, (const char *)accept);
|
||||
strcat(*hsresponse, "\r\n\r\n");
|
||||
|
||||
free(accept);
|
||||
return (0);
|
||||
}
|
||||
56
lib/json.c
Normal file
56
lib/json.c
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <json-c/json.h>
|
||||
|
||||
// Structs for JSON data to parse
|
||||
#include "../include/json.h"
|
||||
|
||||
|
||||
/** @brief Casts a JSON object from string to `Envelope`
|
||||
*
|
||||
* Iterates through the fields of `json` and casts the wanted
|
||||
* fields to `Envelope`. Returns the following codes:
|
||||
* 0 = Success
|
||||
* 1 = Malformed json object
|
||||
* 2 = Missing `payload` field
|
||||
* 3 = Missing `auth` field
|
||||
*
|
||||
* @param json The string to cast
|
||||
* @param Envelope the Envelope struct to cast into
|
||||
*
|
||||
* @returns The status code
|
||||
*/
|
||||
int parse_envelope_json(const char* json, Envelope* envelope) {
|
||||
json_object* parsed = json_tokener_parse(json);
|
||||
if (!parsed) return MISSING_JSON;
|
||||
|
||||
json_object* parsed_payload;
|
||||
if (json_object_object_get_ex(parsed, "payload", &parsed_payload)) {
|
||||
json_object* parsed_sensorId;
|
||||
if (json_object_object_get_ex(parsed_payload, "sensorId", &parsed_sensorId)) {
|
||||
envelope->payload.sensorId = strdup(json_object_get_string(parsed_sensorId));
|
||||
} else {
|
||||
envelope->payload.sensorId = "Nil";
|
||||
}
|
||||
|
||||
json_object* parsed_sensorData;
|
||||
if (json_object_object_get_ex(parsed_payload, "sensorData", &parsed_sensorData)) {
|
||||
envelope->payload.sensorData = strdup(json_object_get_string(parsed_sensorData));
|
||||
} else {
|
||||
envelope->payload.sensorData = "Nil";
|
||||
}
|
||||
} else {
|
||||
return MISSING_PAYLOAD;
|
||||
}
|
||||
|
||||
json_object* parsed_auth;
|
||||
if (json_object_object_get_ex(parsed, "auth", &parsed_auth)) {
|
||||
envelope->auth = strdup(json_object_get_string(parsed_auth));
|
||||
} else {
|
||||
return MISSING_AUTH;
|
||||
}
|
||||
|
||||
json_object_put(parsed);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
364
lib/libtap.c
Normal file
364
lib/libtap.c
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
libtap - Write tests in C
|
||||
Copyright 2012 Jake Gelbman <gelbman@gmail.com>
|
||||
This file is licensed under the LGPL
|
||||
*/
|
||||
|
||||
#define _DEFAULT_SOURCE 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include "libtap.h"
|
||||
|
||||
static int expected_tests = NO_PLAN;
|
||||
static int failed_tests;
|
||||
static int current_test;
|
||||
static char *todo_mesg;
|
||||
|
||||
static char *
|
||||
vstrdupf (const char *fmt, va_list args) {
|
||||
char *str;
|
||||
int size;
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
if (!fmt)
|
||||
fmt = "";
|
||||
size = vsnprintf(NULL, 0, fmt, args2) + 2;
|
||||
str = malloc(size);
|
||||
if (!str) {
|
||||
perror("malloc error");
|
||||
exit(1);
|
||||
}
|
||||
vsprintf(str, fmt, args);
|
||||
va_end(args2);
|
||||
return str;
|
||||
}
|
||||
|
||||
void
|
||||
tap_plan (int tests, const char *fmt, ...) {
|
||||
expected_tests = tests;
|
||||
if (tests == SKIP_ALL) {
|
||||
char *why;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
why = vstrdupf(fmt, args);
|
||||
va_end(args);
|
||||
printf("1..0 ");
|
||||
diag("SKIP %s\n", why);
|
||||
exit(0);
|
||||
}
|
||||
if (tests != NO_PLAN) {
|
||||
printf("1..%d\n", tests);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vok_at_loc (const char *file, int line, int test, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
char *name = vstrdupf(fmt, args);
|
||||
if (!test) {
|
||||
printf("not ");
|
||||
}
|
||||
printf("ok %d", ++current_test);
|
||||
if (*name)
|
||||
printf(" - %s", name);
|
||||
if (todo_mesg) {
|
||||
printf(" # TODO");
|
||||
if (*todo_mesg)
|
||||
printf(" %s", todo_mesg);
|
||||
}
|
||||
printf("\n");
|
||||
if (!test) {
|
||||
printf("# Failed ");
|
||||
if (todo_mesg)
|
||||
printf("(TODO) ");
|
||||
printf("test ");
|
||||
if (*name)
|
||||
printf("'%s'\n# ", name);
|
||||
printf("at %s line %d.\n", file, line);
|
||||
if (!todo_mesg)
|
||||
failed_tests++;
|
||||
}
|
||||
free(name);
|
||||
return test;
|
||||
}
|
||||
|
||||
int
|
||||
ok_at_loc (const char *file, int line, int test, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vok_at_loc(file, line, test, fmt, args);
|
||||
va_end(args);
|
||||
return test;
|
||||
}
|
||||
|
||||
static int
|
||||
mystrcmp (const char *a, const char *b) {
|
||||
return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b);
|
||||
}
|
||||
|
||||
#define eq(a, b) (!mystrcmp(a, b))
|
||||
#define ne(a, b) (mystrcmp(a, b))
|
||||
|
||||
int
|
||||
is_at_loc (const char *file, int line, const char *got, const char *expected,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
int test = eq(got, expected);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vok_at_loc(file, line, test, fmt, args);
|
||||
va_end(args);
|
||||
if (!test) {
|
||||
diag(" got: '%s'", got);
|
||||
diag(" expected: '%s'", expected);
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
int
|
||||
isnt_at_loc (const char *file, int line, const char *got, const char *expected,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
int test = ne(got, expected);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vok_at_loc(file, line, test, fmt, args);
|
||||
va_end(args);
|
||||
if (!test) {
|
||||
diag(" got: '%s'", got);
|
||||
diag(" expected: anything else");
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
int
|
||||
cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
int test = eq(op, "||") ? a || b
|
||||
: eq(op, "&&") ? a && b
|
||||
: eq(op, "|") ? a | b
|
||||
: eq(op, "^") ? a ^ b
|
||||
: eq(op, "&") ? a & b
|
||||
: eq(op, "==") ? a == b
|
||||
: eq(op, "!=") ? a != b
|
||||
: eq(op, "<") ? a < b
|
||||
: eq(op, ">") ? a > b
|
||||
: eq(op, "<=") ? a <= b
|
||||
: eq(op, ">=") ? a >= b
|
||||
: eq(op, "<<") ? a << b
|
||||
: eq(op, ">>") ? a >> b
|
||||
: eq(op, "+") ? a + b
|
||||
: eq(op, "-") ? a - b
|
||||
: eq(op, "*") ? a * b
|
||||
: eq(op, "/") ? a / b
|
||||
: eq(op, "%") ? a % b
|
||||
: diag("unrecognized operator '%s'", op);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vok_at_loc(file, line, test, fmt, args);
|
||||
va_end(args);
|
||||
if (!test) {
|
||||
diag(" %d", a);
|
||||
diag(" %s", op);
|
||||
diag(" %d", b);
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
static int
|
||||
find_mem_diff (const char *a, const char *b, size_t n, size_t *offset) {
|
||||
size_t i;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a || !b)
|
||||
return 2;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (a[i] != b[i]) {
|
||||
*offset = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
cmp_mem_at_loc (const char *file, int line, const void *got,
|
||||
const void *expected, size_t n, const char *fmt, ...)
|
||||
{
|
||||
size_t offset;
|
||||
int diff = find_mem_diff(got, expected, n, &offset);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vok_at_loc(file, line, !diff, fmt, args);
|
||||
va_end(args);
|
||||
if (diff == 1) {
|
||||
diag(" Difference starts at offset %d", offset);
|
||||
diag(" got: 0x%02x", ((const unsigned char *)got)[offset]);
|
||||
diag(" expected: 0x%02x", ((const unsigned char *)expected)[offset]);
|
||||
}
|
||||
else if (diff == 2) {
|
||||
diag(" got: %s", got ? "not NULL" : "NULL");
|
||||
diag(" expected: %s", expected ? "not NULL" : "NULL");
|
||||
}
|
||||
return !diff;
|
||||
}
|
||||
|
||||
int
|
||||
diag (const char *fmt, ...) {
|
||||
va_list args;
|
||||
char *mesg, *line;
|
||||
int i;
|
||||
va_start(args, fmt);
|
||||
if (!fmt) {
|
||||
va_end(args);
|
||||
return 0;
|
||||
}
|
||||
mesg = vstrdupf(fmt, args);
|
||||
line = mesg;
|
||||
for (i = 0; *line; i++) {
|
||||
char c = mesg[i];
|
||||
if (!c || c == '\n') {
|
||||
mesg[i] = '\0';
|
||||
printf("# %s\n", line);
|
||||
if (!c)
|
||||
break;
|
||||
mesg[i] = c;
|
||||
line = mesg + i + 1;
|
||||
}
|
||||
}
|
||||
free(mesg);
|
||||
va_end(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
exit_status () {
|
||||
int retval = 0;
|
||||
if (expected_tests == NO_PLAN) {
|
||||
printf("1..%d\n", current_test);
|
||||
}
|
||||
else if (current_test != expected_tests) {
|
||||
diag("Looks like you planned %d test%s but ran %d.",
|
||||
expected_tests, expected_tests > 1 ? "s" : "", current_test);
|
||||
retval = 2;
|
||||
}
|
||||
if (failed_tests) {
|
||||
diag("Looks like you failed %d test%s of %d run.",
|
||||
failed_tests, failed_tests > 1 ? "s" : "", current_test);
|
||||
retval = 1;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
bail_out (int ignore, const char *fmt, ...) {
|
||||
va_list args;
|
||||
(void) ignore;
|
||||
va_start(args, fmt);
|
||||
printf("Bail out! ");
|
||||
vprintf(fmt, args);
|
||||
printf("\n");
|
||||
va_end(args);
|
||||
exit(255);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
tap_skip (int n, const char *fmt, ...) {
|
||||
char *why;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
why = vstrdupf(fmt, args);
|
||||
va_end(args);
|
||||
while (n --> 0) {
|
||||
printf("ok %d ", ++current_test);
|
||||
diag("skip %s\n", why);
|
||||
}
|
||||
free(why);
|
||||
}
|
||||
|
||||
void
|
||||
tap_todo (int ignore, const char *fmt, ...) {
|
||||
va_list args;
|
||||
(void) ignore;
|
||||
va_start(args, fmt);
|
||||
todo_mesg = vstrdupf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
tap_end_todo () {
|
||||
free(todo_mesg);
|
||||
todo_mesg = NULL;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <regex.h>
|
||||
|
||||
#ifndef MAP_ANONYMOUS
|
||||
#ifdef MAP_ANON
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#else
|
||||
#error "System does not support mapping anonymous pages"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Create a shared memory int to keep track of whether a piece of code executed
|
||||
dies. to be used in the dies_ok and lives_ok macros. */
|
||||
int
|
||||
tap_test_died (int status) {
|
||||
static int *test_died = NULL;
|
||||
int prev;
|
||||
if (!test_died) {
|
||||
test_died = mmap(0, sizeof (int), PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
*test_died = 0;
|
||||
}
|
||||
prev = *test_died;
|
||||
*test_died = status;
|
||||
return prev;
|
||||
}
|
||||
|
||||
int
|
||||
like_at_loc (int for_match, const char *file, int line, const char *got,
|
||||
const char *expected, const char *fmt, ...)
|
||||
{
|
||||
int test;
|
||||
regex_t re;
|
||||
va_list args;
|
||||
int err = regcomp(&re, expected, REG_EXTENDED);
|
||||
if (err) {
|
||||
char errbuf[256];
|
||||
regerror(err, &re, errbuf, sizeof errbuf);
|
||||
fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n",
|
||||
expected, errbuf, file, line);
|
||||
exit(255);
|
||||
}
|
||||
err = regexec(&re, got, 0, NULL, 0);
|
||||
regfree(&re);
|
||||
test = for_match ? !err : err;
|
||||
va_start(args, fmt);
|
||||
vok_at_loc(file, line, test, fmt, args);
|
||||
va_end(args);
|
||||
if (!test) {
|
||||
if (for_match) {
|
||||
diag(" '%s'", got);
|
||||
diag(" doesn't match: '%s'", expected);
|
||||
}
|
||||
else {
|
||||
diag(" '%s'", got);
|
||||
diag(" matches: '%s'", expected);
|
||||
}
|
||||
}
|
||||
return test;
|
||||
}
|
||||
#endif
|
||||
389
lib/sha1.c
Normal file
389
lib/sha1.c
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* sha1.c
|
||||
*
|
||||
* Description:
|
||||
* This file implements the Secure Hashing Algorithm 1 as
|
||||
* defined in FIPS PUB 180-1 published April 17, 1995.
|
||||
*
|
||||
* The SHA-1, produces a 160-bit message digest for a given
|
||||
* data stream. It should take about 2**n steps to find a
|
||||
* message with the same digest as a given message and
|
||||
* 2**(n/2) to find any two messages with the same digest,
|
||||
* when n is the digest size in bits. Therefore, this
|
||||
* algorithm can serve as a means of providing a
|
||||
* "fingerprint" for a message.
|
||||
*
|
||||
* Portability Issues:
|
||||
* SHA-1 is defined in terms of 32-bit "words". This code
|
||||
* uses <stdint.h> (included via "sha1.h" to define 32 and 8
|
||||
* bit unsigned integer types. If your C compiler does not
|
||||
* support 32 bit unsigned integers, this code is not
|
||||
* appropriate.
|
||||
*
|
||||
* Caveats:
|
||||
* SHA-1 is designed to work with messages less than 2^64 bits
|
||||
* long. Although SHA-1 allows a message digest to be generated
|
||||
* for messages of any number of bits less than 2^64, this
|
||||
* implementation only works with messages with a length that is
|
||||
* a multiple of the size of an 8-bit character.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sha1.h>
|
||||
|
||||
/*
|
||||
* Define the SHA1 circular left shift macro
|
||||
*/
|
||||
#define SHA1CircularShift(bits,word) \
|
||||
(((word) << (bits)) | ((word) >> (32-(bits))))
|
||||
|
||||
/* Local Function Prototyptes */
|
||||
void SHA1PadMessage(SHA1Context *);
|
||||
void SHA1ProcessMessageBlock(SHA1Context *);
|
||||
|
||||
/*
|
||||
* SHA1Reset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the SHA1Context in preparation
|
||||
* for computing a new SHA1 message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Reset(SHA1Context *context)
|
||||
{
|
||||
if (!context)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
context->Length_Low = 0;
|
||||
context->Length_High = 0;
|
||||
context->Message_Block_Index = 0;
|
||||
|
||||
context->Intermediate_Hash[0] = 0x67452301;
|
||||
context->Intermediate_Hash[1] = 0xEFCDAB89;
|
||||
context->Intermediate_Hash[2] = 0x98BADCFE;
|
||||
context->Intermediate_Hash[3] = 0x10325476;
|
||||
context->Intermediate_Hash[4] = 0xC3D2E1F0;
|
||||
|
||||
context->Computed = 0;
|
||||
context->Corrupted = 0;
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Result
|
||||
*
|
||||
* Description:
|
||||
* This function will return the 160-bit message digest into the
|
||||
* Message_Digest array provided by the caller.
|
||||
* NOTE: The first octet of hash is stored in the 0th element,
|
||||
* the last octet of hash in the 19th element.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to use to calculate the SHA-1 hash.
|
||||
* Message_Digest: [out]
|
||||
* Where the digest is returned.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Result( SHA1Context *context,
|
||||
uint8_t Message_Digest[SHA1HashSize])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!context || !Message_Digest)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
if (context->Corrupted)
|
||||
{
|
||||
return context->Corrupted;
|
||||
}
|
||||
|
||||
if (!context->Computed)
|
||||
{
|
||||
SHA1PadMessage(context);
|
||||
for(i=0; i<64; ++i)
|
||||
{
|
||||
/* message may be sensitive, clear it out */
|
||||
context->Message_Block[i] = 0;
|
||||
}
|
||||
context->Length_Low = 0; /* and clear length */
|
||||
context->Length_High = 0;
|
||||
context->Computed = 1;
|
||||
|
||||
}
|
||||
|
||||
for(i = 0; i < SHA1HashSize; ++i)
|
||||
{
|
||||
Message_Digest[i] = context->Intermediate_Hash[i>>2]
|
||||
>> 8 * ( 3 - ( i & 0x03 ) );
|
||||
}
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion
|
||||
* of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update
|
||||
* message_array: [in]
|
||||
* An array of characters representing the next portion of
|
||||
* the message.
|
||||
* length: [in]
|
||||
* The length of the message in message_array
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Input( SHA1Context *context,
|
||||
const uint8_t *message_array,
|
||||
unsigned length)
|
||||
{
|
||||
if (!length)
|
||||
{
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
if (!context || !message_array)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
if (context->Computed)
|
||||
{
|
||||
context->Corrupted = shaStateError;
|
||||
|
||||
return shaStateError;
|
||||
}
|
||||
|
||||
if (context->Corrupted)
|
||||
{
|
||||
return context->Corrupted;
|
||||
}
|
||||
while(length-- && !context->Corrupted)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] =
|
||||
(*message_array & 0xFF);
|
||||
|
||||
context->Length_Low += 8;
|
||||
if (context->Length_Low == 0)
|
||||
{
|
||||
context->Length_High++;
|
||||
if (context->Length_High == 0)
|
||||
{
|
||||
/* Message is too long */
|
||||
context->Corrupted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (context->Message_Block_Index == 64)
|
||||
{
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
|
||||
message_array++;
|
||||
}
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1ProcessMessageBlock
|
||||
*
|
||||
* Description:
|
||||
* This function will process the next 512 bits of the message
|
||||
* stored in the Message_Block array.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
|
||||
* Many of the variable names in this code, especially the
|
||||
* single character names, were used because those were the
|
||||
* names used in the publication.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
{
|
||||
const uint32_t K[] = { /* Constants defined in SHA-1 */
|
||||
0x5A827999,
|
||||
0x6ED9EBA1,
|
||||
0x8F1BBCDC,
|
||||
0xCA62C1D6
|
||||
};
|
||||
int t; /* Loop counter */
|
||||
uint32_t temp; /* Temporary word value */
|
||||
uint32_t W[80]; /* Word sequence */
|
||||
uint32_t A, B, C, D, E; /* Word buffers */
|
||||
|
||||
/*
|
||||
* Initialize the first 16 words in the array W
|
||||
*/
|
||||
for(t = 0; t < 16; t++)
|
||||
{
|
||||
W[t] = context->Message_Block[t * 4] << 24;
|
||||
W[t] |= context->Message_Block[t * 4 + 1] << 16;
|
||||
W[t] |= context->Message_Block[t * 4 + 2] << 8;
|
||||
W[t] |= context->Message_Block[t * 4 + 3];
|
||||
}
|
||||
|
||||
for(t = 16; t < 80; t++)
|
||||
{
|
||||
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
||||
}
|
||||
|
||||
A = context->Intermediate_Hash[0];
|
||||
B = context->Intermediate_Hash[1];
|
||||
C = context->Intermediate_Hash[2];
|
||||
D = context->Intermediate_Hash[3];
|
||||
E = context->Intermediate_Hash[4];
|
||||
|
||||
for(t = 0; t < 20; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | ((~B) & D)) + E + W[t] + K[0];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 20; t < 40; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 40; t < 60; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 60; t < 80; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
context->Intermediate_Hash[0] += A;
|
||||
context->Intermediate_Hash[1] += B;
|
||||
context->Intermediate_Hash[2] += C;
|
||||
context->Intermediate_Hash[3] += D;
|
||||
context->Intermediate_Hash[4] += E;
|
||||
|
||||
context->Message_Block_Index = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1PadMessage
|
||||
*
|
||||
|
||||
* Description:
|
||||
* According to the standard, the message must be padded to an even
|
||||
* 512 bits. The first padding bit must be a '1'. The last 64
|
||||
* bits represent the length of the original message. All bits in
|
||||
* between should be 0. This function will pad the message
|
||||
* according to those rules by filling the Message_Block array
|
||||
* accordingly. It will also call the ProcessMessageBlock function
|
||||
* provided appropriately. When it returns, it can be assumed that
|
||||
* the message digest has been computed.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to pad
|
||||
* ProcessMessageBlock: [in]
|
||||
* The appropriate SHA*ProcessMessageBlock function
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
*/
|
||||
|
||||
void SHA1PadMessage(SHA1Context *context)
|
||||
{
|
||||
/*
|
||||
* Check to see if the current message block is too small to hold
|
||||
* the initial padding bits and length. If so, we will pad the
|
||||
* block, process it, and then continue padding into a second
|
||||
* block.
|
||||
*/
|
||||
if (context->Message_Block_Index > 55)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||
while(context->Message_Block_Index < 64)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
|
||||
while(context->Message_Block_Index < 56)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||
while(context->Message_Block_Index < 56)
|
||||
{
|
||||
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the message length as the last 8 octets
|
||||
*/
|
||||
context->Message_Block[56] = context->Length_High >> 24;
|
||||
context->Message_Block[57] = context->Length_High >> 16;
|
||||
context->Message_Block[58] = context->Length_High >> 8;
|
||||
context->Message_Block[59] = context->Length_High;
|
||||
context->Message_Block[60] = context->Length_Low >> 24;
|
||||
context->Message_Block[61] = context->Length_Low >> 16;
|
||||
context->Message_Block[62] = context->Length_Low >> 8;
|
||||
context->Message_Block[63] = context->Length_Low;
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
88
lib/utf8.c
Normal file
88
lib/utf8.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Amazing utf8 decoder & validator grabbed from:
|
||||
* http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
*
|
||||
* All rights goes to the original author.
|
||||
*/
|
||||
|
||||
#include "utf8.h"
|
||||
|
||||
static const uint8_t utf8d[] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
|
||||
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
|
||||
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
|
||||
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
|
||||
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
|
||||
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
|
||||
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
|
||||
};
|
||||
|
||||
static
|
||||
uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
|
||||
uint32_t type = utf8d[byte];
|
||||
|
||||
*codep = (*state != UTF8_ACCEPT) ?
|
||||
(byte & 0x3fu) | (*codep << 6) :
|
||||
(0xff >> type) & (byte);
|
||||
|
||||
*state = utf8d[256 + *state*16 + type];
|
||||
return *state;
|
||||
}
|
||||
|
||||
int is_utf8(uint8_t *s) {
|
||||
uint32_t codepoint, state = 0;
|
||||
|
||||
while (*s)
|
||||
decode(&state, &codepoint, *s++);
|
||||
|
||||
return state == UTF8_ACCEPT;
|
||||
}
|
||||
|
||||
int is_utf8_len(uint8_t *s, size_t len) {
|
||||
uint32_t codepoint, state = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
decode(&state, &codepoint, *s++);
|
||||
|
||||
return state == UTF8_ACCEPT;
|
||||
}
|
||||
|
||||
uint32_t is_utf8_len_state(uint8_t *s, size_t len, uint32_t state) {
|
||||
uint32_t codepoint;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
decode(&state, &codepoint, *s++);
|
||||
|
||||
return state;
|
||||
}
|
||||
20
lib/utils.c
Normal file
20
lib/utils.c
Normal file
@@ -0,0 +1,20 @@
|
||||
/* utils.c */
|
||||
/** @brief Hash `str` to `hash` for use in a switch case
|
||||
*
|
||||
* A string needs to be hashed for it to be used
|
||||
* inside of a switch statement. We want to use a switch
|
||||
* statement instead of an if-else chain for it's
|
||||
* simplicity and lower amount of low-level operations.
|
||||
*
|
||||
* @param str The string to hash
|
||||
*
|
||||
* @returns hash The hashed version of `str`
|
||||
*/
|
||||
unsigned long hash(const char *str) {
|
||||
unsigned long hash = 5381;
|
||||
unsigned long int c;
|
||||
|
||||
while ((c = *str++))
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
return hash;
|
||||
}
|
||||
Reference in New Issue
Block a user