mirror of
https://github.com/samsonjs/lake.git
synced 2026-03-25 08:55:49 +00:00
ditch gpl hash table library for glib
This commit is contained in:
parent
8439041407
commit
0bb7a2ea22
9 changed files with 35 additions and 462 deletions
11
Makefile
11
Makefile
|
|
@ -1,8 +1,13 @@
|
|||
CC=gcc -Wall -g
|
||||
CC = gcc
|
||||
CFLAGS := -Wall -g $(shell pkg-config --cflags glib-2.0)
|
||||
LFLAGS := $(shell pkg-config --libs glib-2.0)
|
||||
|
||||
all: lake
|
||||
|
||||
lake: lake.o env.o hashtab.o int.o string.o sym.o parse.o bool.o list.o
|
||||
lake: lake.o env.o int.o string.o sym.o parse.o bool.o list.o
|
||||
$(CC) $(CFLAGS) $(LFLAGS) $? -o $@
|
||||
|
||||
# use touch to prevent errors in case files do not exist
|
||||
clean:
|
||||
rm *.o lake
|
||||
@touch dummy.o lake
|
||||
rm -f *.o lake
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ A quick and dirty scheme written in C, for fun and to use while reading The Litt
|
|||
Compiling & Running
|
||||
===================
|
||||
|
||||
Portable C, no deps, nothing to configure, no documentation!
|
||||
Portable C, only dep is glib, nothing to configure, no documentation!
|
||||
|
||||
Install glib 2.x using your package manager, for me it's either `brew install glib` or `sudo aptitude install glib`.
|
||||
|
||||
$ make
|
||||
$ ./lake
|
||||
|
|
|
|||
27
env.c
27
env.c
|
|
@ -7,15 +7,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "lake.h"
|
||||
#include "env.h"
|
||||
#include "hashtab.h"
|
||||
|
||||
#define ENV_TABLE_SIZE 64
|
||||
#define SYM_TABLE_SIZE 1024
|
||||
|
||||
static Env *_shared = NULL;
|
||||
|
||||
|
|
@ -38,37 +35,39 @@ Env *env_make(Env *parent)
|
|||
env = malloc(sizeof(Env));
|
||||
if (!env) oom();
|
||||
env->parent = parent;
|
||||
env->bindings = ht_init(ENV_TABLE_SIZE, NULL);
|
||||
env->symbols = ht_init(SYM_TABLE_SIZE, NULL);
|
||||
env->bindings = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
env->symbols = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
return env;
|
||||
}
|
||||
|
||||
LakeVal *env_define(Env *env, char *key, LakeVal *val)
|
||||
{
|
||||
size_t keylen = strlen(key);
|
||||
if (ht_get(env->bindings, key, keylen) != NULL)
|
||||
if (g_hash_table_lookup(env->bindings, key) != NULL)
|
||||
return NULL;
|
||||
|
||||
return VAL_OR_NIL(ht_put(env->bindings, key, keylen, val, VAL_SIZE(val)));
|
||||
val = VAL_OR_NIL(val);
|
||||
g_hash_table_insert(env->bindings, key, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
LakeVal *env_set(Env *env, char *key, LakeVal *val)
|
||||
{
|
||||
size_t keylen = strlen(key);
|
||||
if (ht_get(env->bindings, key, keylen) == NULL)
|
||||
if (g_hash_table_lookup(env->bindings, key) == NULL)
|
||||
return NULL;
|
||||
|
||||
return VAL_OR_NIL(ht_put(env->bindings, key, keylen, val, VAL_SIZE(val)));
|
||||
val = VAL_OR_NIL(val);
|
||||
g_hash_table_insert(env->bindings, key, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
LakeVal *env_get(Env *env, char *key)
|
||||
{
|
||||
return VAL_OR_NIL(ht_get(env->bindings, key, strlen(key)));
|
||||
return VAL_OR_NIL(g_hash_table_lookup(env->bindings, key));
|
||||
}
|
||||
|
||||
int env_is_bound(Env *env, char *key)
|
||||
{
|
||||
return (ht_get(env->bindings, key, strlen(key)) != NULL);
|
||||
return g_hash_table_lookup(env->bindings, key) != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
6
env.h
6
env.h
|
|
@ -10,12 +10,12 @@
|
|||
#ifndef _LAKE_ENV_H
|
||||
#define _LAKE_ENV_H 1
|
||||
|
||||
#include "hashtab.h"
|
||||
#include <glib.h>
|
||||
|
||||
struct env {
|
||||
struct env *parent;
|
||||
hashtab_t *bindings;
|
||||
hashtab_t *symbols;
|
||||
GHashTable *bindings;
|
||||
GHashTable *symbols;
|
||||
};
|
||||
typedef struct env Env;
|
||||
|
||||
|
|
|
|||
310
hashtab.c
310
hashtab.c
|
|
@ -1,310 +0,0 @@
|
|||
/* hashtab.c - Simple, Reliable C Hashtable
|
||||
* Copyright (C) 2007 Christopher Wellons <mosquitopsu@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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "hashtab.h"
|
||||
|
||||
hashtab_t *ht_init (size_t size, int (*hash_func) (void *, size_t, size_t))
|
||||
{
|
||||
hashtab_t *new_ht = (hashtab_t *) malloc (sizeof (hashtab_t));
|
||||
|
||||
new_ht->arr = (hashtab_node_t **) malloc (sizeof (hashtab_node_t *) * size);
|
||||
|
||||
new_ht->size = size;
|
||||
new_ht->count = 0;
|
||||
|
||||
/* all entries are empty */
|
||||
int i = 0;
|
||||
for (i = 0; i < (int) size; i++)
|
||||
{
|
||||
new_ht->arr[i] = NULL;
|
||||
}
|
||||
|
||||
if (hash_func == NULL)
|
||||
new_ht->hash_func = &ht_hash;
|
||||
else
|
||||
new_ht->hash_func = hash_func;
|
||||
|
||||
return new_ht;
|
||||
}
|
||||
|
||||
void *ht_get (hashtab_t * hashtable, void *key, size_t keylen)
|
||||
{
|
||||
int index = ht_hash (key, keylen, hashtable->size);
|
||||
|
||||
if (hashtable->arr[index] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hashtab_node_t *last_node = hashtable->arr[index];
|
||||
while (last_node != NULL)
|
||||
{
|
||||
/* only compare matching keylens */
|
||||
if (last_node->keylen == keylen)
|
||||
{
|
||||
/* compare keys */
|
||||
if (memcmp (key, last_node->key, keylen) == 0)
|
||||
{
|
||||
return last_node->value;
|
||||
}
|
||||
}
|
||||
|
||||
last_node = last_node->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ht_put (hashtab_t * hashtable,
|
||||
void *key, size_t keylen, void *value, size_t vallen)
|
||||
{
|
||||
int index = ht_hash (key, keylen, hashtable->size);
|
||||
|
||||
hashtab_node_t *next_node, *last_node;
|
||||
next_node = hashtable->arr[index];
|
||||
last_node = NULL;
|
||||
|
||||
/* Search for an existing key. */
|
||||
while (next_node != NULL)
|
||||
{
|
||||
/* only compare matching keylens */
|
||||
if (next_node->keylen == keylen)
|
||||
{
|
||||
/* compare keys */
|
||||
if (memcmp (key, next_node->key, keylen) == 0)
|
||||
{
|
||||
/* this key already exists, replace it */
|
||||
if (next_node->vallen != vallen)
|
||||
{
|
||||
/* new value is a different size */
|
||||
free (next_node->value);
|
||||
next_node->value = malloc (vallen);
|
||||
if (next_node->value == NULL)
|
||||
return NULL;
|
||||
}
|
||||
memcpy (next_node->value, value, vallen);
|
||||
next_node->vallen = vallen;
|
||||
return next_node->value;
|
||||
}
|
||||
}
|
||||
|
||||
last_node = next_node;
|
||||
next_node = next_node->next;
|
||||
}
|
||||
|
||||
/* create a new node */
|
||||
hashtab_node_t *new_node;
|
||||
new_node = (hashtab_node_t *) malloc (sizeof (hashtab_node_t));
|
||||
if (new_node == NULL)
|
||||
return NULL;
|
||||
|
||||
/* get some memory for the new node data */
|
||||
new_node->key = malloc (keylen);
|
||||
new_node->value = malloc (vallen);
|
||||
if (new_node->key == NULL || new_node->key == NULL)
|
||||
{
|
||||
free (new_node->key);
|
||||
free (new_node->value);
|
||||
free (new_node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy over the value and key */
|
||||
memcpy (new_node->key, key, keylen);
|
||||
memcpy (new_node->value, value, vallen);
|
||||
new_node->keylen = keylen;
|
||||
new_node->vallen = vallen;
|
||||
|
||||
/* no next node */
|
||||
new_node->next = NULL;
|
||||
|
||||
/* Tack the new node on the end or right on the table. */
|
||||
if (last_node != NULL)
|
||||
last_node->next = new_node;
|
||||
else
|
||||
hashtable->arr[index] = new_node;
|
||||
|
||||
hashtable->count++;
|
||||
return new_node->value;
|
||||
}
|
||||
|
||||
/* delete the given key from the hashtable */
|
||||
void ht_remove (hashtab_t * hashtable, void *key, size_t keylen)
|
||||
{
|
||||
hashtab_node_t *last_node, *next_node;
|
||||
int index = ht_hash (key, keylen, hashtable->size);
|
||||
next_node = hashtable->arr[index];
|
||||
last_node = NULL;
|
||||
|
||||
while (next_node != NULL)
|
||||
{
|
||||
if (next_node->keylen == keylen)
|
||||
{
|
||||
/* compare keys */
|
||||
if (memcmp (key, next_node->key, keylen) == 0)
|
||||
{
|
||||
/* free node memory */
|
||||
free (next_node->value);
|
||||
free (next_node->key);
|
||||
|
||||
/* adjust the list pointers */
|
||||
if (last_node != NULL)
|
||||
last_node->next = next_node->next;
|
||||
else
|
||||
hashtable->arr[index] = next_node->next;
|
||||
|
||||
/* free the node */
|
||||
free (next_node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
last_node = next_node;
|
||||
next_node = next_node->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* grow the hashtable */
|
||||
void *ht_grow (hashtab_t * old_ht, size_t new_size)
|
||||
{
|
||||
/* create new hashtable */
|
||||
hashtab_t *new_ht = ht_init (new_size, old_ht->hash_func);
|
||||
if (new_ht == NULL)
|
||||
return NULL;
|
||||
|
||||
void *ret; /* captures return values */
|
||||
|
||||
/* Iterate through the old hashtable. */
|
||||
hashtab_iter_t ii;
|
||||
ht_iter_init (old_ht, &ii);
|
||||
for (; ii.key != NULL; ht_iter_inc (&ii))
|
||||
{
|
||||
ret = ht_put (new_ht, ii.key, ii.keylen, ii.value, ii.vallen);
|
||||
if (ret == NULL)
|
||||
{
|
||||
/* Insert failed. Destroy new hashtable and return. */
|
||||
ht_destroy (new_ht);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroy the old hashtable. */
|
||||
ht_destroy (old_ht);
|
||||
|
||||
return new_ht;
|
||||
}
|
||||
|
||||
/* free all resources used by the hashtable */
|
||||
void ht_destroy (hashtab_t * hashtable)
|
||||
{
|
||||
hashtab_node_t *next_node, *last_node;
|
||||
|
||||
/* Free each linked list in hashtable. */
|
||||
int i;
|
||||
for (i = 0; i < (int) hashtable->size; i++)
|
||||
{
|
||||
next_node = hashtable->arr[i];
|
||||
while (next_node != NULL)
|
||||
{
|
||||
/* destroy node */
|
||||
free (next_node->key);
|
||||
free (next_node->value);
|
||||
last_node = next_node;
|
||||
next_node = next_node->next;
|
||||
free (last_node);
|
||||
}
|
||||
}
|
||||
|
||||
free (hashtable->arr);
|
||||
free (hashtable);
|
||||
}
|
||||
|
||||
/* iterator initilaize */
|
||||
void ht_iter_init (hashtab_t * hashtable, hashtab_iter_t * ii)
|
||||
{
|
||||
/* stick in initial bookeeping data */
|
||||
ii->internal.hashtable = hashtable;
|
||||
ii->internal.node = NULL;
|
||||
ii->internal.index = -1;
|
||||
|
||||
/* have iterator point to first element */
|
||||
ht_iter_inc (ii);
|
||||
}
|
||||
|
||||
/* iterator increment */
|
||||
void ht_iter_inc (hashtab_iter_t * ii)
|
||||
{
|
||||
hashtab_t *hashtable = ii->internal.hashtable;
|
||||
int index = ii->internal.index;
|
||||
|
||||
/* attempt to grab the next node */
|
||||
if (ii->internal.node == NULL || ii->internal.node->next == NULL)
|
||||
index++;
|
||||
else
|
||||
{
|
||||
/* next node in the list */
|
||||
ii->internal.node = ii->internal.node->next;
|
||||
ii->key = ii->internal.node->key;
|
||||
ii->value = ii->internal.node->value;
|
||||
ii->keylen = ii->internal.node->keylen;
|
||||
ii->vallen = ii->internal.node->vallen;
|
||||
return;
|
||||
}
|
||||
|
||||
/* find next node */
|
||||
while (hashtable->arr[index] == NULL && index < (int) hashtable->size)
|
||||
index++;
|
||||
|
||||
if (index >= (int) hashtable->size)
|
||||
{
|
||||
/* end of hashtable */
|
||||
ii->internal.node = NULL;
|
||||
ii->internal.index = (int) hashtable->size;
|
||||
|
||||
ii->key = NULL;
|
||||
ii->value = NULL;
|
||||
ii->keylen = 0;
|
||||
ii->vallen = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* point to the next item in the hashtable */
|
||||
ii->internal.node = hashtable->arr[index];
|
||||
ii->internal.index = index;
|
||||
ii->key = ii->internal.node->key;
|
||||
ii->value = ii->internal.node->value;
|
||||
ii->keylen = ii->internal.node->keylen;
|
||||
ii->vallen = ii->internal.node->vallen;
|
||||
}
|
||||
|
||||
int ht_hash (void *key, size_t keylen, size_t hashtab_size)
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
/* very simple hash function for now */
|
||||
int i;
|
||||
for (i = 0; i < (int) keylen; i++)
|
||||
{
|
||||
sum += ((unsigned char *) key)[i] * (i + 1);
|
||||
}
|
||||
|
||||
return (sum % (int) hashtab_size);
|
||||
}
|
||||
120
hashtab.h
120
hashtab.h
|
|
@ -1,120 +0,0 @@
|
|||
/* hashtab.h - Simple, Reliable C Hashtable
|
||||
* Copyright (C) 2007 Christopher Wellons <mosquitopsu@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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/* I needed a hashtable for a project and wanted to code my own. This
|
||||
* hashtable compromises speed for reliability, specifically with
|
||||
* growing the hashtable.
|
||||
*
|
||||
* The hashtable does not grow automatically, but when the hashtable
|
||||
* grow function is called. Growing the hashtable is a safe operation:
|
||||
* if growing the hashtable fails, the existing hashtable is not
|
||||
* destroyed or modified.
|
||||
*
|
||||
* This hashtable is not thread-safe.
|
||||
*/
|
||||
|
||||
#ifndef HASHTAB_H
|
||||
#define HASHTAB_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct hashtab_node_t
|
||||
{
|
||||
void *key; /* key for the node */
|
||||
size_t keylen; /* length of the key */
|
||||
void *value; /* value for this node */
|
||||
size_t vallen; /* length of the value */
|
||||
|
||||
struct hashtab_node_t *next; /* next node (open hashtable) */
|
||||
} hashtab_node_t;
|
||||
|
||||
typedef struct hashtab_t
|
||||
{
|
||||
hashtab_node_t **arr;
|
||||
size_t size; /* size of the hash */
|
||||
int count; /* number if items in this table */
|
||||
int (*hash_func) (void *, size_t, size_t); /* hash function */
|
||||
} hashtab_t;
|
||||
|
||||
/* Iterator type for iterating through the hashtable. */
|
||||
typedef struct hashtab_iter_t
|
||||
{
|
||||
/* key and value of current item */
|
||||
void *key;
|
||||
void *value;
|
||||
size_t keylen;
|
||||
size_t vallen;
|
||||
|
||||
/* bookkeeping data */
|
||||
struct hashtab_internal_t
|
||||
{
|
||||
hashtab_t *hashtable;
|
||||
hashtab_node_t *node;
|
||||
int index;
|
||||
} internal;
|
||||
|
||||
} hashtab_iter_t;
|
||||
|
||||
/* Initialize a new hashtable (set bookingkeeping data) and return a
|
||||
* pointer to the hashtable. A hash function may be provided. If no
|
||||
* function pointer is given (a NULL pointer), then the built in hash
|
||||
* function is used. A NULL pointer returned if the creation of the
|
||||
* hashtable failed due to a failed malloc(). */
|
||||
hashtab_t *ht_init (size_t size,
|
||||
int (*hash_func)
|
||||
(void *key, size_t keylen, size_t ht_size));
|
||||
|
||||
/* Fetch a value from table matching the key. Returns a pointer to
|
||||
* the value matching the given key. */
|
||||
void *ht_get (hashtab_t * hashtable, void *key, size_t keylen);
|
||||
|
||||
/* Put a value into the table with the given key. Returns NULL if
|
||||
* malloc() fails to allocate memory for the new node. */
|
||||
void *ht_put (hashtab_t * hashtable,
|
||||
void *key, size_t keylen, void *value, size_t vallen);
|
||||
|
||||
/* Delete the given key and value pair from the hashtable. If the key
|
||||
* does not exist, no error is given. */
|
||||
void ht_remove (hashtab_t * hashtable, void *key, size_t keylen);
|
||||
|
||||
/* Change the size of the hashtable. It will allocate a new hashtable
|
||||
* and move all keys and values over. The pointer to the new hashtable
|
||||
* is returned. Will return NULL if the new hashtable fails to be
|
||||
* allocated. If this happens, the old hashtable will not be altered
|
||||
* in any way. The old hashtable is destroyed upon a successful
|
||||
* grow. */
|
||||
void *ht_grow (hashtab_t * hashtable, size_t new_size);
|
||||
|
||||
/* Free all resources used by the hashtable. */
|
||||
void ht_destroy (hashtab_t * hashtable);
|
||||
|
||||
/* Initialize the given iterator. It will point to the first element
|
||||
* in the hashtable. */
|
||||
void ht_iter_init (hashtab_t * hashtable, hashtab_iter_t * ii);
|
||||
|
||||
/* Increment the iterator to the next element. The iterator key and
|
||||
* value will point to NULL values when the iterator has reached the
|
||||
* end of the hashtable. */
|
||||
void ht_iter_inc (hashtab_iter_t * ii);
|
||||
|
||||
/* Default hashtable hash function. */
|
||||
int ht_hash (void *key, size_t key_size, size_t hashtab_size);
|
||||
|
||||
#endif
|
||||
1
list.c
1
list.c
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "int.h"
|
||||
#include "lake.h"
|
||||
#include "list.h"
|
||||
|
|
|
|||
3
string.c
3
string.c
|
|
@ -7,6 +7,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "int.h"
|
||||
|
|
@ -61,8 +62,6 @@ char *str_val(LakeStr *str)
|
|||
return strdup(str->s);
|
||||
}
|
||||
|
||||
#define MIN(a, b) (a < b ? a : b)
|
||||
|
||||
LakeInt *str_cmp(LakeStr *a, LakeStr *b)
|
||||
{
|
||||
return int_from_c(strncmp(a->s, b->s, MIN(a->n, b->n)));
|
||||
|
|
|
|||
15
sym.c
15
sym.c
|
|
@ -7,12 +7,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "env.h"
|
||||
#include "lake.h"
|
||||
#include "hashtab.h"
|
||||
#include "string.h"
|
||||
#include "sym.h"
|
||||
|
||||
|
|
@ -28,15 +28,14 @@ static LakeSym *sym_alloc(void)
|
|||
|
||||
LakeSym *sym_intern(char *s)
|
||||
{
|
||||
size_t n = strlen(s);
|
||||
hashtab_t *symbols = shared_env()->symbols;
|
||||
LakeSym *sym = ht_get(symbols, s, n);
|
||||
GHashTable *symbols = shared_env()->symbols;
|
||||
LakeSym *sym = g_hash_table_lookup(symbols, s);
|
||||
if (!sym) {
|
||||
sym = sym_alloc();
|
||||
sym->n = n;
|
||||
sym->n = strlen(s);
|
||||
sym->s = strdup(s);
|
||||
sym->hash = ht_hash(s, n, symbols->size);
|
||||
ht_put(symbols, sym->s, sym->n, sym, VAL_SIZE(sym));
|
||||
sym->hash = g_str_hash(s);
|
||||
g_hash_table_insert(symbols, sym->s, sym);
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
|
|
@ -61,8 +60,6 @@ unsigned long sym_val(LakeSym *sym)
|
|||
return sym->hash;
|
||||
}
|
||||
|
||||
#define MIN(a, b) (a < b ? a : b)
|
||||
|
||||
LakeSym *sym_eq(LakeSym *a, LakeSym *b)
|
||||
{
|
||||
return bool_from_int(strncmp(a->s, b->s, MIN(a->n, b->n)) == 0);
|
||||
|
|
|
|||
Loading…
Reference in a new issue