libclc-developers Mailing List for libclc (Page 5)
Status: Planning
Brought to you by:
augestad
You can subscribe to this list here.
| 2003 |
Jan
|
Feb
(1) |
Mar
(170) |
Apr
(73) |
May
(3) |
Jun
(1) |
Jul
(1) |
Aug
|
Sep
(2) |
Oct
|
Nov
|
Dec
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(1) |
Dec
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-25 14:53:00
|
Sorry about the empty message.
Jan Engelhardt writes:
>> int clc_ultostr(char *ptr, size_t size, unsigned long num, int base)
>> {
>> (...)
>> /* Point to the last char in buffer */
> Prefering //
C89 only supports '/**/'. '//' is C99/C++.
>> } while (num > 0 && sp != ptr);
'while (num && sp != ptr)' is enough.
>> if(num > 0)
>> return 0;
>> else if(ptr != sp)
>> memmove(ptr, sp, size - (sp - ptr));
Please memmove in either case. Return 1 (or length of string) if no
truncation, 0 if there is a truncated result:
if(ptr != sp)
memmove(ptr, sp, size - (sp - ptr));
return !num;
--
Hallvard
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-25 14:44:20
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-25 11:30:59
|
Bj=F8rn Augestad writes:
>Hallvard B Furuseth wrote:
>=20
>> char *clc_stpcpy(char *CLC_RESTRICT s1, const char *CLC_RESTRICT s2)
>> {
>> clc_assert_not_null(clc_stpcpy, s1);
>> clc_assert_not_null(clc_stpcpy, s2);
>=20
> A general question: Given the fact that the asserts are for the libclc=20
> *user* only, how about an assert like this one?
>
> clc_assert_arg(clc_stpcpy, s1 !=3D s2);
>
> I just use you code as an example here.
In this case I don't think so, because that condition is incomplete.
The full test is something like !(s1 <=3D s2 && s2 <=3D s1+strlen(s1)), and
I don't like that either because of the slowdown from strlen. Some
people say one should only use the debug version of libclc while testing
code, and in that case assert slowdown may not be important, but there
are people (e.g. FSF, I think) who release code with asserts turned on
so that they can get good error reports from their users.
I've fixed the other asserts like you said.
>> char *clc_stralloc(const char *arg1, ...)
>> {
>> size_t len;
>> const char *arg;
>> char *ret, *end;
>> va_list ap;
>> int bad;
>>=20
>> bad =3D 0;
>> len =3D 1;
>=20
> How about "int bad =3D 0;" on one line instead?
Well, I just wrote it in what I hope will be the libclc style. I like
initializations to be close to the block which uses the values when
possible, which would usually have to be separate statements since there
will usually be asserts between the declarations and the rest of the
code. Doesn't matter since there are no asserts here of course, so I
could have moved the len and bad declarations down and added
initializers. Feel free to do so yourself if you want.
--=20
Hallvard
|
|
From: Bryan D. <bd...@bd...> - 2003-03-24 22:36:15
|
=2D----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Sunday 23 March 2003 09:14 pm, Peter "Shaggy" Haywood wrote: > From: Bryan Donlan <bd...@bd...> > Subject: Re: [Libclc-developers] Introducing the double linked list > interface > >How about FindPrev to walk the list backwards? > > That's what the dir member (of CLC_DL_SEARCH_DATA) is for. This indicat= es > whether to search forward or backward, and is set by clc_dl_FIND_FIRST(). No, undoing FindNext. The lack of such a function defeats much of the purpo= se=20 of a doubly-linked list. You have to walk the entire list just to look at t= he=20 prior node. > > What about removing arbitrar=3D > >y=3D20 > >nodes and replacing them during such a search? > > Huh? I don't understand. Walk through the list, and if some test is true, delete the current node an= d=20 continue to the next. =2D----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.1 (GNU/Linux) iD8DBQE+f4hax533NjVSos4RAhRfAJ9Vy88s/VpmCq/z70Vncuhj6e0iHQCfW0fX B7QS8YEbWIkXGqGXLd2OQZY=3D =3D3qu7 =2D----END PGP SIGNATURE----- |
|
From: <bo...@me...> - 2003-03-24 21:29:49
|
Hallvard B Furuseth wrote:
> Here are the string functions I have added. See the previously posted
> doc for detailed descriptions.
>
> /* Copyright(c) 2003, Hallvard B. Furuseth. (h.b...@us...) */
> char *clc_stpcpy(char *CLC_RESTRICT s1, const char *CLC_RESTRICT s2)
> {
> clc_assert_not_null(clc_stpcpy, s1);
> clc_assert_not_null(clc_stpcpy, s2);
A general question: Given the fact that the asserts are for the libclc
*user* only, how about an assert like this one?
clc_assert_arg(clc_stpcpy, s1 != s2);
I just use you code as an example here.
>
> char *
> clc_strendcpy(char *dst, const char *CLC_RESTRICT src, const char *end)
> {
> clc_assert_arg(clc_strendcpy, dst != NULL && src != NULL && end != NULL);
If I was the user of libclc, I'd prefer to get the exact name of the
invalid argument. This kind of assert forces me to check up to three
variables. The line is also longer than 72 characters.
> if (dst < end) {
> end--; /* room for terminating NUL */
> while (dst < end && *src)
> *dst++ = *src++;
> *dst = '\0';
> if (*src)
> dst++; /* return end parameter if truncation */
> }
> return dst;
> }
>
> char *clc_stralloc(const char *arg1, ...)
> {
> size_t len;
> const char *arg;
> char *ret, *end;
> va_list ap;
> int bad;
>
> bad = 0;
> len = 1;
How about "int bad = 0;" on one line instead?
>
> int
> clc_strcasecmp(const char *s1, const char *s2)
> {
Asserts?
>
> int
> clc_strncasecmp(const char *s1, const char *s2, size_t n)
> {
Asserts?
> char *
> clc_strsep(char **CLC_RESTRICT stringp, const char *CLC_RESTRICT delim)
> {
> char *str, *end;
> clc_assert_arg(clc_strsep, delim != NULL);
clc_assert_not_null()?
>
> char *
> clc_strtok_r(char *str, const char *CLC_RESTRICT delim, char **tracker)
> {
> clc_assert_arg(clc_strtok_r, delim != NULL);
> clc_assert_arg(clc_strtok_r, tracker != NULL);
clc_assert_not_null()?
Looks very good! Just a few asserts away from beeing production ready. ;-)
--
boa
libclc home: http://libclc.sourceforge.net
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-24 19:07:48
|
Here are the string functions I have added. See the previously posted
doc for detailed descriptions.
clc_stpcpy - copy string and return pointer to end
/* $Id: clc_stpcpy.c,v 1.1 2003/03/22 09:17:33 augestad Exp $ */
#include <stddef.h>
#include "clc_assert.h"
#include "clc_string.h"
/* Copyright(c) 2003, Hallvard B. Furuseth. (h.b...@us...) */
char *clc_stpcpy(char *CLC_RESTRICT s1, const char *CLC_RESTRICT s2)
{
clc_assert_not_null(clc_stpcpy, s1);
clc_assert_not_null(clc_stpcpy, s2);
while ((*s1 = *s2++) != '\0')
s1++;
return s1;
}
clc_strendcpy - copy limited string and return pointer to end
/* $Id: clc_strendcpy.c,v 1.2 2003/03/23 09:55:11 hfuru Exp $ */
/*
* Copyright(c) 2003, Howard Chu <hy...@hi...>
*/
#include <stddef.h>
#include "clc_assert.h"
#include "clc_string.h"
char *
clc_strendcpy(char *dst, const char *CLC_RESTRICT src, const char *end)
{
clc_assert_arg(clc_strendcpy, dst != NULL && src != NULL && end != NULL);
if (dst < end) {
end--; /* room for terminating NUL */
while (dst < end && *src)
*dst++ = *src++;
*dst = '\0';
if (*src)
dst++; /* return end parameter if truncation */
}
return dst;
}
clc_stralloc - concatenate arguments, ending with (char*)0, to a new
malloced string
/* $Id: clc_stralloc.c,v 1.4 2003/03/22 14:34:21 hfuru Exp $ */
/*
* Copyright(c) 2003, Hallvard B. Furuseth <h.b...@us...>.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "clc_string.h"
char *clc_stralloc(const char *arg1, ...)
{
size_t len;
const char *arg;
char *ret, *end;
va_list ap;
int bad;
bad = 0;
len = 1;
va_start(ap, arg1);
for (arg = arg1; arg != (char *)0; arg = va_arg(ap, char *)) {
size_t arglen = strlen(arg);
len += arglen;
if(len < arglen) {
/* string length too large for size_t */
bad = 1;
break;
}
}
va_end(ap);
if(bad)
return NULL;
ret = malloc(len);
if(ret != NULL) {
end = ret;
*end = '\0';
va_start(ap, arg1);
for (arg = arg1; arg != (char *)0; arg = va_arg(ap, char *))
end = clc_stpcpy(end, arg);
va_end(ap);
}
return ret;
}
clc_strcasecmp - case-insensitive string compare
/* $Id: clc_strcasecmp.c,v 1.1 2003/03/23 17:13:02 hfuru Exp $ */
/*
* Copyright(c) 2003, Hallvard B. Furuseth <h.b...@us...>.
*/
#include <ctype.h>
#include "clc_assert.h"
#include "clc_string.h"
int
clc_strcasecmp(const char *s1, const char *s2)
{
for (;; s1++, s2++) {
int c1, c2;
c1 = tolower((unsigned char)*s1);
c2 = tolower((unsigned char)*s2);
if (c1 == '\0' || c1 != c2)
return c1 - c2;
}
}
clc_strcasecmp - case-insensitive string compare with size limit
/* $Id: clc_strncasecmp.c,v 1.1 2003/03/23 17:13:02 hfuru Exp $ */
/*
* Copyright(c) 2003, Hallvard B. Furuseth <h.b...@us...>.
*/
#include <ctype.h>
#include "clc_assert.h"
#include "clc_string.h"
int
clc_strncasecmp(const char *s1, const char *s2, size_t n)
{
for (; n--; s1++, s2++) {
int c1 = tolower((unsigned char)*s1);
int c2 = tolower((unsigned char)*s2);
if (c1 == '\0' || c1 != c2)
return c1 - c2;
}
return 0;
}
clc_strsep, clc_strtok_r - extract token from string, return token
/* $Id: clc_strsep.c,v 1.1 2003/03/22 12:19:29 hfuru Exp $ */
/*
* Copyright (C) 2003, Hallvard B. Furuseth <h.b...@us...>.
*/
#include <string.h>
#include "clc_string.h"
#include "clc_assert.h"
char *
clc_strsep(char **CLC_RESTRICT stringp, const char *CLC_RESTRICT delim)
{
char *str, *end;
clc_assert_arg(clc_strsep, delim != NULL);
clc_assert_arg(clc_strsep, stringp != NULL && *stringp != NULL);
str = *stringp + strspn(*stringp, delim);
if (*str == '\0') {
end = str;
str = NULL;
} else {
end = str + strcspn(str, delim);
if (*end != '\0')
*end++ = '\0';
}
*stringp = end;
return str;
}
char *
clc_strtok_r(char *str, const char *CLC_RESTRICT delim, char **tracker)
{
clc_assert_arg(clc_strtok_r, delim != NULL);
clc_assert_arg(clc_strtok_r, tracker != NULL);
clc_assert_arg(clc_strtok_r, str != NULL || *tracker != NULL);
if (str != NULL)
*tracker = str;
return clc_strsep(tracker, delim);
}
--
Hallvard
|
|
From: <pha...@al...> - 2003-03-24 01:12:08
|
Sorry for taking so long to reply! Been a bit busy over the last few days.
>Peter Shaggy Haywood wrote:
>> I submit the following for discussion and (hopefully) the approval of the
>> group. This may also serve as a first draft of the documentation for this
>> module.
>
>Thanks for a very well documented submission, Peter.
>
>Here are my comments, questions and misunderstandings :-)
>
>- Have you considered to provide the cleanup function (dtor) to the
>delete-functions instead of storing the dtor in the list? I guess that
>you currently store it since we need a per-node dtor right now.
That's exactly the reason. Each payload type could have its own cleanup
function. And, in theory, even each node could have its own cleanup
function, even if other nodes with the same payload type have different
ones. I could have had the clc_dl_FreeNode() function take a pointer to the
cleanup function, which would have worked on a node by node basis. But that
wouldn't have been very useful when deleting the whole list using
clc_dl_FreeList(). I needed a way to provide each node with its own cleanup
function, and the logical approach was to store this in the node when it is
created.
>- Why do we need the size of the payload?
Payloads, even of the same type, may be of different sizes. For example,
strings. Sure you can find the length of a string with strlen(), but other
types of data aren't so easy to deal with. We need to know the size of each
payload in order to copy the payload (clc_dl_Payload()) or to compare it to
a search key (clc_dl_Find*()).
>> I'll let you ponder this interface and suggest improvements before I post
>> the implementation of this module.
>
>The plan is to add the list to the June release of libclc. We may want
>to discuss other "container classes" as well as the list so that common
>container concepts gets the same solution. This means that we won't
>finish this discussion in a long time...
>
>>
>> This document explains how to use the functions and data types defined by
>> the clc_dl module. This module implements a generic double linked list
>> system that is easy to use but flexible.
>>
>> Data types:
>> ===========
>> These are the data types defined by this module. They are defined in the
>> clc_dl.h header file. These types should generally not be touched directly
>> by users. Leave it to the module to manipulate these types. However, their
>> definitions are provided to users by defining CLC_DL_ADVANCED_FUNCS before
>> including the header. This allows extra flexibility and control.
>
>IMO, the CLC_DL_ADVANCED_FUNCS macro should be removed. All
>functionality should always be available, just like the standard C library.
I disagree. I think it's best to hide the inner workings of the data types
from the user for normal use, but provide access to them when extra
flexibility is really required. The data types and "advanced" functions are
still there, they're just hidden from the user when the macro is not
defined. It's safer that way.
>> typedef struct CLC_DL_NODE
>> {
>> struct CLC_DL_NODE *prev, *next;
>> int type; /* payload type */
>> size_t size; /* payload size */
>> void *payload; /* payload data */
>> void (*cleanup)(void *); /* cleanup function */
>> } CLC_DL_NODE;
>
>Does CLC_DL_NODE and CLC_DL_LIST have to be in the header?
I assume you mean the actual definitions of those struct types. Yes. Both
clc_dl.c and clc_dl_a.c need them. They're also needed when the user wants
to mess about with the internals of these types. Where else would they be
but in the header?
>> CLC_DL_NODE is the basis of the system. It is the double linked list node
>> type. The members of this struct are as follows:
>>
>> prev, next pointers to the previous and next nodes
>>
>> type the payload type (in case of a linked list containing
>> more than one type of data
>
>How likely is it that we have more than one type in the same list? If it
>isn't used a lot, maybe we can have more than one list type? CLC_DL_LIST
>and CLC_DL_MULTILIST, where the latter 'knows' about types?
That's a possibility. I prefer the simplicity of my way, though. Most
linked lists may not have multiple types, but it's there in case it's
needed, without complicating matters by defining another list ADT without a
type member (which would probably require duplication of most of the code).
>Even if we have more than one type in the list, does the list have to
>know? A user can easily create a wrapper for the data.
> struct type_wrapper {
> int type;
> void *data;
> };
>and store this in the list.
That's a point. We could do that. But I think the user should have to
worry about as little as possible (or practical, at least). It's no trouble
at all for the library to support multiple types. So IMHO it would be better
to leave it the way it is. It would mean unnecessary trouble to the user to
have to define and wrap his data in such a wrapper stucture.
>> size size of the payload
>
>Why do we need the size? If it is needed for copying, what about pointer
>members in the payload?
I don't know what you mean by "pointer members in the payload". How can
pointer members help with copying? If you mean make the payload point at
already existing data, I don't like that approach. It just makes more sense
to me to copy the payload data into memory "owned" by the node.
Besides, the size may also be needed for comparing.
>> payload pointer to the actual payload itself
>>
>> cleanup pointer to a user supplied function to cleanup any extra
>> memory associated with a node's payload (but not the
>> node or payload itself - clc_dl_FreeNode() takes care of
>> that)
>>
>>
>> typedef struct CLC_DL_LIST
>> {
>> CLC_DL_NODE *head, *tail; /* first and last nodes */
>> size_t num; /* number of nodes */
>> int dyn; /* flag to indicate that this struct was
>> dynamically allocated */
>
>Aren't CLC_DL_LIST objects always dynamically allocated?
No, I originally allowed them to be statically allocated. (AAMOF, in the
original version of the lib, before preparing it for libclc, that was the
only way to do it.) However, since posting that I have reevaluated the
approach. Now I think the dyn member should be dropped and CLC_DL_LIST
objects should always be dynamically allocated. That makes it simpler, less
for both the users and developers of libclc to worry about.
>> } CLC_DL_LIST;
>>
>> CLC_DL_LIST is a structure intended to store all data needed to access and
>> manipulate a linked list, currently comprising the following:
>>
>> head, tail pointers to the first and last nodes in the list (NULL
>> if list is empty)
>>
>> num number of nodes in the list
>>
>> dyn flag to indicate whether this struct was dynamically
>> allocated with clc_dl_NewList() -
>> IMPORTANT NOTE: don't tamper with this member or memory
>> faults may occur!
>>
>> Most accesses to a linked list use this struct. An object of this type may
>> be obtained (dynamically allocated) by using the clc_dl_NewList() function.
>>
>>
>> struct CLC_DL_SEARCH_DATA
>> {
>> CLC_DL_LIST *list; /* list the search applys to */
>> CLC_DL_NODE *lastnode; /* last node found by search */
>> int type; /* type of payload to search for */
>> size_t size; /* size of payload to search for */
>> void *find; /* payload data to search for */
>> int (*cmp)(int keytype, size_t keysize, const void *keydata,
>> int nodetype, size_t nodesize,
>> const void *nodedata); /* compare func */
>> int dir; /* direction of search:
>> CLC_DL_FORWARD = search forward,
>> CLC_DL_REVERSE = search backward */
>
>How about "int forward" instead of "int dir". If forward == 0, then it
>is backward. It removes the need for CLC_DL_FORWARD and CLC_DL_REVERSE.
Actually, I've made it reverse if dir is negative and forward otherwise.
This makes the most sense to me. That could be changed in the way you
suggest, I guess. But I don't see any point in changing it. The macros
CLC_DL_FORWARD and CLC_DL_REVERSE are just a convenience to the user. I have
now clarified this in the documentation.
>> };
>[snip]
>
>>
>> The clc_dl_FindNext() function uses this data to continue the search using
>> the same criteria, and updates the lastnode member each time it is called.
>> Users should obtain a pointer to a CLC_DL_SEARCH_DATA object, by using
>> clc_dl_NewSearch(), and pass it to clc_dl_FindFirst() and clc_dl_FindNext()
>> to search for nodes.
>
>I, as everyone else, have my own list :-) This list adt has a concept of
>sublists. A sublist can be created in different ways, e.g. as a result
>set from a search within the original list. Then the user can traverse
>the sublist. IMHO this is better than FindFirst/Next since the user can
>manipulate the result set. The sublist can for example be sorted before
>traversal.
That's an idea. Though I guess a sublist would need its own nodes (since
each node only has pointers to one next node and one previous node), rather
than use the same nodes. It would make sense, then, to simply create a new
list and copy the nodes (or payloads) that will populate the new (sub)list.
A copynode() function would be useful.
Another possible way to implement a sublist is not really to create
another list at all, but use an array of pointers to CLC_DL_NODE, each
element containing the address of one of the nodes in the main list. You
couldn't insert and delete nodes in one easily, though. But I imagine you
wouldn't usually want to do so with a sublist. And swapping nodes and
sorting the sublist becomes trivial.
>See http://www.metasystems.no/metalib/list_8h.html#_details for more
>details.
That looks much like the "create a new list and copy nodes" approach
described above.
>> Basic functions:
>> ================
>> The following are the "basic" functions, the ones most commonly used, in
>> the clc_dl module. These are defined in the clc_dl.c file. "Advanced"
>> functions, which provide extra flexibility but are less commonly used, are
>> described below this section.
>>
>>
>> CLC_DL_LIST *clc_dl_NewList(void)
>>
>> The clc_dl_NewList() function is used to dynamically allocate and
>> initialise a CLC_DL_LIST structure. All fields are initialised to reflect an
>> empty linked list (one with no nodes).
>>
>>
>> CLC_DL_NODE *clc_dl_InsertData(CLC_DL_LIST *list,
>> int dir,
>> int type,
>> size_t size,
>> void *payload,
>> void (*cleanup)(void *));
>
>clc_dl_add() or clc_dl_insert()?
Possible. The former doesn't really make it obvious (to me, at least) that
this function inserts data. The latter is also slightly ambiguous. It could
be confused with clc_dl_InsertNodes(), which inserts a node or nodes that
have already been created with clc_dl_NewNode(). This function inserts brand
new data. (It calls clc_dl_NewNode() and clc_dl_InsertNodes() behind the
scenes.) I suggest we leave the name of this function as it is.
>[snip]
>
>> CLC_DL_SEARCH_DATA clc_dl_NewSearch(void);
>> void clc_dl_FreeSearch(CLC_DL_SEARCH_DATA *sd);
>
>What if we want to search in e.g. a clc_map, clc_deque or some other
>container? Is it possible to create a common descriptor for all/most
>searchable modules?
Yes, that might be possible. Good idea!
>> clc_dl_NewSearch() dynamically allocates a CLC_DL_SEARCH_DATA structure
>> that can be used by clc_dl_FindFirst() and clc_dl_FindNext().
>> clc_dl_FreeSearch() is used to de-allocate this structure.
>>
>>
>> CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list,
>> int type,
>> size_t size,
>> void *find,
>> int (*cmp)(int typekey,
>> const void *datakey,
>> int typenode,
>> const void *datanode),
>> int dir,
>> CLC_DL_SEARCH_DATA *srch);
>>
>> CLC_DL_NODE *clc_dl_FindNext(CLC_DL_SEARCH_DATA *srch);
>
>Later you have clc_dl_findall(). How about clc_dl_findfirst() and
>clc_dl_findnext(), or just clc_dl_first(), clc_dl_next() and clc_dl_all()?
I prefer to keep the current names. They're clearer. They show the user
that they are search functions. clc_dl_first(), for example, is less clear,
IMO. It's not obvious that it is a search function. We've all had moments of
fatigue, lacking clarity of mind, when we've tried to guess what a function
does rather than look it up in the documentation. Why make it harder?
>[snip]
>
>> void *clc_dl_ModifyPayload(CLC_DL_NODE *node,
>> int newtype,
>> size_t newsize,
>> void *newpayload,
>> void (*newcleanup)(void *));
>
>clc_dl_replace()?
That is a possibility.
>It doesn't really modify the existing payload, but replaces an existing
>payload with a new.
Yes, true.
>> Comparison functions:
>> ---------------------
>> A comparison function, as used by clc_dl_FindFirst() and
>> clc_dl_FindNext(), compares one payload type and data with another. It must
>> have the following prototype:
>>
>> int cmp(int, size_t, const void *, int, size_t, const void *);
>>
>> The first three parameters represent key values used to compare against the
>> node payload represented by the last three parameters. The int parameters
>> represent the payload type. This is useful in the case of a linked list that
>> contains more than one type of data. The comparison of data can be skipped
>> if the types don't match. The arguments passed to these parameters may
>> simply be ignored if the linked list only contains one type of data. The
>> size_t parameters represent the size of the data. The const void *
>> parameters are pointers to the data to be compared. The function must return
>> 0 to indicate a match and non-zero to indicate no match.
>
>How about -1, 0 and 1 for less than, equal and greater than? Then we can
>sort the list using the same function.
Yes, I was just thinking about that this morning, actually. I agree with
you on this one.
>[snip]
>
>I miss a few functions. copy(), merge(), foreach(),
I'll get onto these. We probably need two copy functions, one for nodes
and one for whole lists. Or do we not need to copy whole lists?
>BTW, I spent 5 hours on a train yesterday thinking about libclc. One of
>the things I thought of was a general object adaptor which could be used
>in "advanced" modules. Such an object adaptor would know enough about an
>object to allocate, free, compare, copy and clone any object. I do *not*
>recommend that we use such an adaptor in clc_list as the concept will
>probably be too odd for most people. Maybe it can be used in another
>list, clc_objlist?
Something like this but not quite so complicated might be useful. A
payload struct containing all the payload data my library currently keeps in
nodes: the type and size of the payload (data), a pointer to the payload
(data) itself, and a pointer to a cleanup function. This could be included
in all libclc container ADT modules.
|
|
From: <pha...@al...> - 2003-03-24 01:11:52
|
From: Bryan Donlan <bd...@bd...> Subject: Re: [Libclc-developers] Introducing the double linked list interface >On Sunday 16 March 2003 10:39 pm, Peter "Shaggy" Haywood wrote: >> I submit the following for discussion and (hopefully) the approval of t= >he >> group. This may also serve as a first draft of the documentation for this >> module. >> I'll let you ponder this interface and suggest improvements before I po= >st >> the implementation of this module. >> >> This document explains how to use the functions and data types defined = >by >> the clc_dl module. This module implements a generic double linked list >> system that is easy to use but flexible. >> >> Data types: >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >> These are the data types defined by this module. They are defined in the >> clc_dl.h header file. These types should generally not be touched directly >> by users. Leave it to the module to manipulate these types. However, their >> definitions are provided to users by defining CLC_DL_ADVANCED_FUNCS before >> including the header. This allows extra flexibility and control. >> >> >> typedef struct CLC_DL_NODE > >I don't think struct of typedef names should be uppercase. IMO, only enums = >and=20 >#defines should be uppercase. I disagree. But this is open for discussion. If enough people agree with you, I can change it. >> CLC_DL_NODE *clc_dl_InsertData(CLC_DL_LIST *list, >> int dir, >> int type, >> size_t size, >> void *payload, >> void (*cleanup)(void *)); >> >> The clc_dl_InsertData() function is used to create a new node and insert >> new data into a linked list. It returns a pointer to the new node, or a >> null pointer if memory could not be allocated for the node. The parameters >> are: >> >> list a pointer indicating the linked to insert data into >> >> dir direction of insertion: use CLC_DL_FRONT or CLC_DL_START >> to insert at the start of the list or CLC_DL_END to >> insert at the end of the list > >How about inserting after a given node? I've been thinking about that. Perhaps we need a clc_dl_InsertDataAt() function, which has an extra parameter, a pointer to a CLC_DL_NODE and inserts the new node before or after (depending on the dir argument) the indicated node. >> type type of the payload (in case of a list containing more >> than one type of data) >> >> size size of the payload >> >> payload the data to be copied to the new node's payload >> >> cleanup pointer to a user supplied function to cleanup any extra >> memory associated with a node's payload (but not the >> node or payload itself - clc_dl_FreeNode() takes care of >> that) > >Why can't the caller do that? Unless you'll cann it with the payload, of=20 >course. But it dosen't say that. I want to free the user from the burden of freeing nodes and lists. Basically, the user shouldn't even have to deal with nodes directly. They can if they want to, by defining CLC_DL_ADVANCED_FUNCS. But they shouldn't be forced to mess about with the internals of the data types. That's the whole point of a library, really; to free the user from such responsibilities. Of course, there will be some things the library simply can't forsee, such as the fact that a node's payload contains pointers (for example), and the memory they point at must be freed. That's why a user defined cleanup function is needed. But for the above reason, it is best to have the lib call this function, rather than force the user to access the node or its payload directly. >> CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list, >> int type, >> size_t size, >> void *find, >> int (*cmp)(int typekey, >> const void *datakey, >> int typenode, >> const void *datanode), >> int dir, >> CLC_DL_SEARCH_DATA *srch); >> >> CLC_DL_NODE *clc_dl_FindNext(CLC_DL_SEARCH_DATA *srch); >> >> The clc_dl_FindFirst() and clc_dl_FindNext() functions are used to sear= >ch >> linearly for a node or nodes of a specified type containing a specified s= >et >> of data. The parameters of the former are: >> >> list pointer indicating the linked list to search >> >> type type of payload to search for >> >> size size of payload to search for >> >> find data to search for in payloads >> >> cmp pointer to compare function (compares key type, size and >> data to node's payload type, size and data - returns 0 >> if match, non-zero otherwise) >> >> dir direction of search: use CLC_DL_FORWARD to search >> forward from the start of the list or CLC_DL_REVERSE to >> search backward from the end of the list >> >> srch pointer to a structure containing data used in the >> search process >> >> clc_dl_FindFirst() searches for the first node that matches (according to >> the comparison function passed as the fifth argument) the key data and its >> type and size. clc_dl_FindNext(), using a CLC_DL_SEARCH_DATA * previously >> passed to clc_dl_FindFirst(), searches for subsequent nodes that match the >> search criteria specified in the clc_dl_FindFirst() call. >> A special comparison function has been provided, which always compares >> equal, called clc_dl_FindAll(). This can be used by clc_dl_FindFirst() and >> clc_dl_FindNext() to "find" all nodes of a list in turn. This provides a >> way to traverse the list node by node. >> clc_dl_FindFirst() returns a pointer to the first node found that match= >es >> the search criteria. clc_dl_FindNext() returns a pointer to the next node >> in turn that matches the search criteria. Both functions return a null >> pointer if a match is not found. > >How about FindPrev to walk the list backwards? That's what the dir member (of CLC_DL_SEARCH_DATA) is for. This indicates whether to search forward or backward, and is set by clc_dl_FIND_FIRST(). > What about removing arbitrar= >y=20 >nodes and replacing them during such a search? Huh? I don't understand. >> Advanced functions: >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >> The following functions are defined in the clc_dl_a.c source file. Thier >> prototypes are available by defining a macro called CLC_DL_ADVANCED_FUNCS >> before the clc_dl.h header is included. > >What about GetNext and GetPrev? Good idea! Those might be useful. I'll add them. From: Michael B.Allen <mb...@io...> Subject: Re: [Libclc-developers] Introducing the double linked list interface >On Mon, 17 Mar 2003 19:37:01 -0500 >Bryan Donlan <bd...@bd...> wrote: > >> > typedef struct CLC_DL_NODE >> >> I don't think struct of typedef names should be uppercase. IMO, only enums and >> #defines should be uppercase. > >I second that. It appears we need a style document or a consistent >codebase to model the code after. I asked about this in comp.lang.c, but the only response I got seemed to indicate that it should be left up to the author whether type names would be upper or lower case. I agree that there should be a reasonably consistent look to the libclc code. I prefer to have upper case type names, but I can live with lower case ones. If the consensus is to have lower case names, then I'll change the names to lower case. >> > CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list, >> > int type, >> > size_t size, >> > void *find, >> > int (*cmp)(int typekey, >> > const void *datakey, >> > int typenode, >> > const void *datanode), >> > int dir, >> > CLC_DL_SEARCH_DATA *srch); > >This is far too complicated. Also these callback interfaces are less >pleasant than a simple iterator interface because you frequently operate >on variable on the stack. I don't see what's complicated about it. The first three parameters represent the search key. The callback is analogous to that used by bsearch() and qsort(). The next parameter represents the direction of searching (forward or backward). And the last parameter is for saving the search data for clc_dl_FindNext() to use. All this seems perfectly simple to me. >I would just have an iterate function that initializes the search context >object and a next object that returns each element. I don't see the >advantage of returning the first element with the initial call. It's >a little awkward programatically actually. Not to me. I don't see why you have a problem with it. |
|
From: Bryan D. <bd...@bd...> - 2003-03-23 19:53:58
|
=2D----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Sunday 23 March 2003 02:51 pm, Bj=F8rn Augestad wrote: > Bryan Donlan wrote: > > How about an saprintf? > > > > size_t saprintf(char *buf, const char *formatstr, void **args); > > size_t sanprintf(char *buf, size_t size, const char *formatstr, void > > **args); > > > > Put simply, it works like sprintf, but takes arguments in a void * arra= y. > > Arguments which are not pointers must be stored and passed as a pointer > > to said storage. Or would this go in another module? > > > > Example: > > char buf[1024]; > > char str1[1024] =3D "The number is: "; > > char str2[1024] =3D "!"; > > > > int x =3D 42; > > void *args[3]; > > args[0] =3D str1; > > args[1] =3D &x; > > args[2] =3D str2; > > > > sanprintf(buf, 1024, "%s%d%s\n", args); > > > > This would be useful if the format string is built at runtime - e.g. for > > an interpreter for some other language. > > Interesting function. The concept may even solve a (unreleated) problem > I've had for some time, so thank you. > > If you want clc_sanprintf to make it into clc_string now, we need a lot > more from you. We need a full implementation, documentation according to > Bertrands requirements and a working test program. Then the function > must survive the discussion here as well as on c.l.c. :-) We could adapt code from the BSD C library - it's under the same licence. I= 'll=20 look into it. =2D----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.1 (GNU/Linux) iD8DBQE+fhDRx533NjVSos4RAlSYAJ9SP6rOLFak0W90MvGuse8rZi/jpwCgrfIw WMRDTAzcWd5OSRjQwtVJ1Js=3D =3DZJrH =2D----END PGP SIGNATURE----- |
|
From: <bo...@me...> - 2003-03-23 19:51:26
|
Bryan Donlan wrote: > How about an saprintf? > > size_t saprintf(char *buf, const char *formatstr, void **args); > size_t sanprintf(char *buf, size_t size, const char *formatstr, void > **args); > > Put simply, it works like sprintf, but takes arguments in a void * array. > Arguments which are not pointers must be stored and passed as a pointer to > said storage. Or would this go in another module? > > Example: > char buf[1024]; > char str1[1024] = "The number is: "; > char str2[1024] = "!"; > > int x = 42; > void *args[3]; > args[0] = str1; > args[1] = &x; > args[2] = str2; > > sanprintf(buf, 1024, "%s%d%s\n", args); > > This would be useful if the format string is built at runtime - e.g. for an > interpreter for some other language. Interesting function. The concept may even solve a (unreleated) problem I've had for some time, so thank you. If you want clc_sanprintf to make it into clc_string now, we need a lot more from you. We need a full implementation, documentation according to Bertrands requirements and a working test program. Then the function must survive the discussion here as well as on c.l.c. :-) -- boa libclc home: http://libclc.sourceforge.net Re: [Libclc-developers] clc_strings: last call for functions |
|
From: Bryan D. <bd...@bd...> - 2003-03-23 18:03:06
|
=2D----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Sunday 23 March 2003 11:48 am, Bj=F8rn Augestad wrote: > The plan is to release libclc-0.2 in the middle of April. Before that > comp.lang.c must approve on both the interface and implementation of the > functions in clc_string. I expect that process to take some time. ;-) > > The deadline for submitting functions is therefore set to 2003-03-25 > 16:00 GMT. If you have a function which *must* be a part of the module, > please submit it, including documentation and a small test program, to > this list before the deadline. How about an saprintf? size_t saprintf(char *buf, const char *formatstr, void **args); size_t sanprintf(char *buf, size_t size, const char *formatstr, void **args= ); Put simply, it works like sprintf, but takes arguments in a void * array.=20 Arguments which are not pointers must be stored and passed as a pointer to= =20 said storage. Or would this go in another module? Example: char buf[1024]; char str1[1024] =3D "The number is: "; char str2[1024] =3D "!"; int x =3D 42; void *args[3]; args[0] =3D str1; args[1] =3D &x; args[2] =3D str2; sanprintf(buf, 1024, "%s%d%s\n", args); This would be useful if the format string is built at runtime - e.g. for an= =20 interpreter for some other language. =2D----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.1 (GNU/Linux) iD8DBQE+ffbTx533NjVSos4RAlkIAKCZAfpwOJH2tM8P42fvj750CjPY2QCfROc/ mpdunT7ZNOqd/bSJD6OB2jg=3D =3DLRwI =2D----END PGP SIGNATURE----- |
|
From: <bo...@me...> - 2003-03-23 17:56:32
|
Hallvard B Furuseth wrote:
> Bjørn Augestad writes:
>>Speaking of optimizations, any opinions on code like this?
>>
>>int clc_ultostr(char *ptr, size_t size, unsigned long num, int base)
>>{
>> const char *sym = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
>> char* sp;
>>
>>#ifdef CLC_FAST
>> if(base == 2) { (...) return num == 0; }
>>#endif
>> .... /* regular version goes here */
>>}
>
>
> Since the _whole_ base2 function is special-cased, I think it's better
> to provide a separate base2 function. Then the library still gets
> bigger like you say, but not the application using it.
>
Hmm. A separate base2 function means that the user must call different
functions depending on the speed/size tradeoff he's willing to make. We
discussed the concept of CLC_FAST early in the project, where the
intention of CLC_FAST was to allow for speedier implementations if the
user/builder of libclc wanted that.
I used base2 as an easy-to-implement example, pretty sure that if the
idea catches on, others can provide optimizations for e.g. base 16. We
don't want to call base2, base16, base10 or clc_ultostr() depending on
base, do we?
What if we add clc_ltostr(), clc_lltostr(), clc_itostr() and
clc_ulltostr() for other integer types? Do we want separate base2
functions for them as well?
--
boa
libclc home: http://libclc.sourceforge.net
|
|
From: Bryan D. <bd...@bd...> - 2003-03-23 17:56:32
|
=2D----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Sunday 23 March 2003 11:16 am, Hallvard B Furuseth wrote: > Bj=F8rn Augestad writes: > > But the functions without a length parameter may have its place in > > libclc. We can just exclude the problematic ones. :-) > > That's why I wrote "Source code for clc_strncasecomm() follows. The > others are for the time being left as an exercise for the reader." > I just posted the most complicated one, the others can be implemented > by removing some tests or function calls. > > > Here is one possible implementation, submitted for the sake of > > discussion only. > > > > (...) > > > > size_t clc_strcomm(const char* s1, const char* s2) > > Well, I already said why I think it's wrong to return size_t: > I think equal strings should return -1. If the strings have a common part at least LONG_MAX characters long, we have undefined behavior. Not so with size_t. =2D----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.1 (GNU/Linux) iD8DBQE+ffU2x533NjVSos4RAsHfAJwIdL5dUQUr35MP1LPNXuysSpFcvwCgqeIv 2Bd+QSd0xY3WYZSrDYtUycw=3D =3D3Oeg =2D----END PGP SIGNATURE----- |
|
From: Hallvard B F. <h.b...@us...> - 2003-03-23 17:19:32
|
Bj=F8rn Augestad writes:
> How about renaming clc_strnrev() to clc_memrev()?
Done.
> Oh well, your version looks better so we=20
> keep that one. Please update libclc/src/string/clc_strrev.c
Done.
> Speaking of optimizations, any opinions on code like this?
>=20
> int clc_ultostr(char *ptr, size_t size, unsigned long num, int base)
> {
> const char *sym =3D "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
> char* sp;
>=20
> #ifdef CLC_FAST
> if(base =3D=3D 2) { (...) return num =3D=3D 0; }
> #endif
> .... /* regular version goes here */
> }
Since the _whole_ base2 function is special-cased, I think it's better
to provide a separate base2 function. Then the library still gets
bigger like you say, but not the application using it.
--=20
Hallvard
|
|
From: <bo...@me...> - 2003-03-23 16:48:20
|
The plan is to release libclc-0.2 in the middle of April. Before that comp.lang.c must approve on both the interface and implementation of the functions in clc_string. I expect that process to take some time. ;-) The deadline for submitting functions is therefore set to 2003-03-25 16:00 GMT. If you have a function which *must* be a part of the module, please submit it, including documentation and a small test program, to this list before the deadline. TIA -- boa Please join the libclc-developers list at http://lists.sourceforge.net/lists/listinfo/libclc-developers |
|
From: Hallvard B F. <h.b...@us...> - 2003-03-23 16:22:05
|
Bj=F8rn Augestad writes: >Hallvard B Furuseth wrote: >> >> The problem is what to return if the strings are equal and shorter than >> the length parameter. strlen(s1)? Then the caller must test if either >> of s1[return value] or s2[return value] is non-'\0' if he wants to know >> if the strings are equal. strlen(s1)+1? Then we can't inspect >> s1[return value], which might be out of bounds, and we must test >> if the return value is nonzero before inspecting s1[return value-1]. >> So I gave up and returned -1 instead. >=20 > But the functions without a length parameter may have its place in=20 > libclc. We can just exclude the problematic ones. :-) Sorry, last answer was a little quick. The explanation above applies both to the functions with a length parameter and the ones without. I posted clc_strncasecmp only because that has all the code that the others have, and all the problems that the others have. --=20 Hallvard |
|
From: Hallvard B F. <h.b...@us...> - 2003-03-23 16:17:02
|
Bj=F8rn Augestad writes: > But the functions without a length parameter may have its place in=20 > libclc. We can just exclude the problematic ones. :-) That's why I wrote "Source code for clc_strncasecomm() follows. The others are for the time being left as an exercise for the reader." I just posted the most complicated one, the others can be implemented by removing some tests or function calls. >=20 > Here is one possible implementation, submitted for the sake of=20 > discussion only. >=20 > (...) >=20 > size_t clc_strcomm(const char* s1, const char* s2) Well, I already said why I think it's wrong to return size_t: I think equal strings should return -1. --=20 Hallvard |
|
From: <bo...@me...> - 2003-03-23 16:12:07
|
Hallvard B Furuseth wrote:
>
> The problem is what to return if the strings are equal and shorter than
> the length parameter. strlen(s1)? Then the caller must test if either
> of s1[return value] or s2[return value] is non-'\0' if he wants to know
> if the strings are equal. strlen(s1)+1? Then we can't inspect
> s1[return value], which might be out of bounds, and we must test
> if the return value is nonzero before inspecting s1[return value-1].
> So I gave up and returned -1 instead.
>
But the functions without a length parameter may have its place in
libclc. We can just exclude the problematic ones. :-)
Here is one possible implementation, submitted for the sake of
discussion only.
#include <stddef.h>
#include <ctype.h>
size_t clc_strcomm(const char* s1, const char* s2)
{
size_t n = 0;
while(*s1 != '\0' && *s2 != '\0' && *s1 == *s2) {
n++;
s1++;
s2++;
}
return n;
}
size_t clc_strcasecomm(const char* s1, const char* s2)
{
size_t n = 0;
while(*s1 != '\0' && *s2 != '\0'
&& tolower((unsigned char)*s1) == tolower((unsigned char)*s2)) {
n++;
s1++;
s2++;
}
return n;
}
--
boa
Please join the libclc-developers list
at http://lists.sourceforge.net/lists/listinfo/libclc-developers
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-23 15:47:54
|
Bj=F8rn Augestad writes: > 1. Why do you return -1 if s1 is shorter than s2? I don't. the *s1 =3D=3D '\0' test only hits if *s1 =3D=3D *s2. > 2. The implementation crashes if s2 is shorter than s1 and shorter than=20 > len, doesn't it? Then the tolower test will stop the loop. Well, unless *s2 =3D=3D '\0' and tolower(*s2) !=3D '\0', but if anyone has broken tolower that badly I don't care if they lose. > 3. Why have a len at all? If you just want to see if the first n=20 > characters are common, can't memcmp, strncasecmp or others be used instea= d? One could test with strncasecmp first and then call strcasecomm() if the strings are not equal, but that's more work and more code. > 4. Given 1) and 3), why not return size_t? What can go wrong? The problem is what to return if the strings are equal and shorter than the length parameter. strlen(s1)? Then the caller must test if either of s1[return value] or s2[return value] is non-'\0' if he wants to know if the strings are equal. strlen(s1)+1? Then we can't inspect s1[return value], which might be out of bounds, and we must test if the return value is nonzero before inspecting s1[return value-1]. So I gave up and returned -1 instead. --=20 Hallvard |
|
From: <bo...@me...> - 2003-03-23 15:35:33
|
Hallvard B Furuseth wrote:
>>I think you should post the nice functions as well. Never know if
>>someone needs them or suddenly finds a new way of doing things. :-)
>
>
> OK, here are the clc_str{n}{case}comm() functions. Does anyone need
> them? I don't:-)
I have had use for functions like this. If you want to implement
strtod() you must support the text "infinity", which can be abbreviated
to "inf" and should be treated case insensitive as well.
n = clc_strcasecomm(s, "infinity");
would have been nice to have in such cases.
I have some questions about the design. Allow for stupid questions here,
hard to grasp everything at first try.
1. Why do you return -1 if s1 is shorter than s2? IMO that means that
the length of the common part == strlen(s1).
2. The implementation crashes if s2 is shorter than s1 and shorter than
len, doesn't it?
3. Why have a len at all? If you just want to see if the first n
characters are common, can't memcmp, strncasecmp or others be used instead?
4. Given 1) and 3), why not return size_t? What can go wrong? If either
of the strings are "", then return 0. When a char differs, return count
so far.
>
> NAME
> clc_strcomm, clc_strncomm, clc_strcasecomm, clc_strncasecomm
> - find common prefix of strings
>
> SYNOPSIS
> #include <clc_string.h>
> long clc_strcomm(const char *s1, const char *s2);
> long clc_strncomm(const char *s1, const char *s2, long len);
> long clc_strcasecomm(const char *s1, const char *s2);
> long clc_strncasecomm(const char *s1, const char *s2, long len);
>
> DESCRIPTION
> These functions find the length of the common prefix of two strings.
>
> clc_strcomm() and clc_strncomm() do case-sensitive compare,
> clc_strcasecomm() and clc_strncasecomm() case-insensitive.
>
> clc_strcomm() and clc_strcasecomm() compare until the terminating
> null characters. clc_strncomm() and clc_strncasecomm() also stop
> after len characters compared equal.
>
> If the strings are longer than LONG_MAX, the behaviors of
> clc_strncomm() and clc_strncasecomm() are undefined.
>
> RETURN VALUES
> The functions return the common length of the strings, or -1 if the
> strings compare equal.
>
> CAVEATS
> Note that the functions treat the size as long, not size_t.
>
> SEE ALSO
> clc(3), strcmp(3), strncmp(3), clc_strcasecmp(3), clc_strncasecmp(3)
>
>
> Implementation choices:
> - I used long instead of size_t as a length parameter to emphasize that
> a long length is returned.
> - I could have returned an error condition (-2?) from clc_str{case}cmp
> if the strings were equal for more than LONG_MAX characters, but
> didn't bother. I can't imagine these functions being used on other
> than fairly short strings.
>
> Source code for clc_strncasecomm() follows. The others are for the time
> being left as an exercise for the reader.
>
>
> /* $Id$ */
> /*
> * Copyright(c) 2003 Hallvard B Furuseth <h.b...@us...>
> */
>
> #include <ctype.h>
> #include <clc_assert.h>
> #include <clc_string.h>
>
> long
> clc_strncasecomm(const char *s1, const char *s2, long len)
> {
> const char *start = s1;
>
> clc_assert_arg(clc_strncasecomm, s1 != NULL && s2 != NULL);
>
> for (; --len >= 0; ++s1, ++s2) {
> if (tolower((unsigned char)*s1) != tolower((unsigned char)*s2))
> return s1 - start;
> if (*s1 == '\0')
> break;
> }
> return -1;
> }
>
[snip]
--
boa
Please join the libclc-developers list
at http://lists.sourceforge.net/lists/listinfo/libclc-developers
|
|
From: <bo...@me...> - 2003-03-23 14:50:59
|
Hallvard B Furuseth wrote:
> Bjørn Augestad writes:
>
>>Some sales pitches for clc_strrev. :-)
>
>
> OK.
>
> In that case, here is a smaller version. I added clc_strnrev() too
> since I imagine that can be useful sometimes, or at least faster when
> we already know strlen.
How about renaming clc_strnrev() to clc_memrev()? That's what it does
and suddenly we have a general purpose memory reverser. It can even be
used to swap bytes in integers.
>
> BTW, I did as you said and replaced #include "clc_*" with <clc_*>:-)
That was in the user documentation. We must use "" to avoid that we
include headers from a previous version already installed. Nice try,
though ;-)
>
> Note: The 'if(len)' is a very slight optimization, it can be omitted
> (and the end-- moved) at the price of an extra loop for uneven-length
> strings.
>
> /* $Id$ */
> /*
> * Copyright(c) 2003, Hallvard B Furuseth <h.b...@us...>
I compared the two versions and optimized mine a little. Turned out that
mine was slightly faster on a 600MHz Pentium running Linux. Then I tried
it on an Athlon 1800+ running Windows XP. Then your version was faster.
Finally Windows *crashed*. :-( Oh well, your version looks better so we
keep that one. Please update libclc/src/string/clc_strrev.c
Speaking of optimizations, any opinions on code like this?
int clc_ultostr(char *ptr, size_t size, unsigned long num, int base)
{
const char *sym = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char* sp;
#ifdef CLC_FAST
if(base == 2) {
sp = ptr;
size--;
do {
*sp++ = num & 0x01 ? '1' : '0';
num >>= 1;
} while(num > 0 && size-- > 0) ;
if(num == 0) {
*sp = '\0';
clc_strrev(ptr);
}
return num == 0;
}
#endif
.... /* regular version goes here */
}
The CLC_FAST version is 4 times faster than the regular one, but the
library will be bigger. Nice for those who likes to print bitpatterns?
Strangely enough division by bitshifting is still faster than / 2. (gcc
3.2.1 -O3 -NDEBUG)
--
boa
Please join the libclc-developers list
at http://lists.sourceforge.net/lists/listinfo/libclc-developers
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-23 14:33:14
|
> I think you should post the nice functions as well. Never know if
> someone needs them or suddenly finds a new way of doing things. :-)
OK, here are the clc_str{n}{case}comm() functions. Does anyone need
them? I don't:-)
NAME
clc_strcomm, clc_strncomm, clc_strcasecomm, clc_strncasecomm
- find common prefix of strings
SYNOPSIS
#include <clc_string.h>
long clc_strcomm(const char *s1, const char *s2);
long clc_strncomm(const char *s1, const char *s2, long len);
long clc_strcasecomm(const char *s1, const char *s2);
long clc_strncasecomm(const char *s1, const char *s2, long len);
DESCRIPTION
These functions find the length of the common prefix of two strings.
clc_strcomm() and clc_strncomm() do case-sensitive compare,
clc_strcasecomm() and clc_strncasecomm() case-insensitive.
clc_strcomm() and clc_strcasecomm() compare until the terminating
null characters. clc_strncomm() and clc_strncasecomm() also stop
after len characters compared equal.
If the strings are longer than LONG_MAX, the behaviors of
clc_strncomm() and clc_strncasecomm() are undefined.
RETURN VALUES
The functions return the common length of the strings, or -1 if the
strings compare equal.
CAVEATS
Note that the functions treat the size as long, not size_t.
SEE ALSO
clc(3), strcmp(3), strncmp(3), clc_strcasecmp(3), clc_strncasecmp(3)
Implementation choices:
- I used long instead of size_t as a length parameter to emphasize that
a long length is returned.
- I could have returned an error condition (-2?) from clc_str{case}cmp
if the strings were equal for more than LONG_MAX characters, but
didn't bother. I can't imagine these functions being used on other
than fairly short strings.
Source code for clc_strncasecomm() follows. The others are for the time
being left as an exercise for the reader.
/* $Id$ */
/*
* Copyright(c) 2003 Hallvard B Furuseth <h.b...@us...>
*/
#include <ctype.h>
#include <clc_assert.h>
#include <clc_string.h>
long
clc_strncasecomm(const char *s1, const char *s2, long len)
{
const char *start = s1;
clc_assert_arg(clc_strncasecomm, s1 != NULL && s2 != NULL);
for (; --len >= 0; ++s1, ++s2) {
if (tolower((unsigned char)*s1) != tolower((unsigned char)*s2))
return s1 - start;
if (*s1 == '\0')
break;
}
return -1;
}
#ifdef CLC_TEST
#include <stdio.h>
int
main()
{
if(clc_strncasecomm("abc", "ABD", 3) != 2 ||
clc_strncasecomm("abc", "ABD", 2) != -1 ||
clc_strncasecomm("abc", "ABD", 1) != -1 ||
clc_strncasecomm("ab", "ABD", 3) != 2 ||
clc_strncasecomm("abc", "DEF", 3) != 0 ) {
puts("Sorry.");
return 1;
}
return 0;
}
#endif /* CLC_TEST */
--
Hallvard
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-23 13:40:41
|
Bj=F8rn Augestad writes:
> Some sales pitches for clc_strrev. :-)
OK.
In that case, here is a smaller version. I added clc_strnrev() too
since I imagine that can be useful sometimes, or at least faster when
we already know strlen.
BTW, I did as you said and replaced #include "clc_*" with <clc_*>:-)
Note: The 'if(len)' is a very slight optimization, it can be omitted
(and the end-- moved) at the price of an extra loop for uneven-length
strings.
/* $Id$ */
/*
* Copyright(c) 2003, Hallvard B Furuseth <h.b...@us...>
*/
#include <string.h>
#include <clc_assert.h>
#include <clc_string.h>
char *
clc_strrev(char *s)
{
clc_assert_not_null(clc_strrev, s);
return clc_strnrev(s, strlen(s));
}
char *
clc_strnrev(char *s, size_t len)
{
clc_assert_not_null(clc_strnrev, s);
if(len) {
char *beg =3D s;
char *end =3D s + len - 1;
while (beg < end) {
char tmp =3D *end;
*end-- =3D *beg;
*beg++ =3D tmp;
}
}
return s;
}
--=20
Hallvard
|
|
From: <bo...@me...> - 2003-03-23 12:25:02
|
Hallvard B Furuseth wrote:
> Bjørn Augestad writes:
>
>>We can't have a string module without a strrev function, can we?
>>I could not find a proposal for one on google, so here is my version.
>>q1: Do we need it?
>
>
> I don't think so. I can only remember needing a rev function once or
> twice in my life. I have heroically refrained from posting a few nice
> libclc functions I've rarely if ever needed myself:-)
>
Some sales pitches for clc_strrev. :-)
1. It seems to be a common problem for newbies to reverse a string, lots
of people have asked about this over the years. We can help them out by
providing it.
2. Microsoft has one (_strrev), ANSI C does not. clc_strrev can aid
portability.
3. Some functions can be implemented in a clearer way if we have a
clc_strrev. Consider the clc_ultostr() which in the latest version
writes from the end of buffer to the beginning and then calls memmove to
adjust the buffer. clc_ultostr() could be changed to writing from the
beginning of the buffer and then just reverse the output before
returning. Here's a quick&dirty implementation :
int clc_ultostr(char *ptr, size_t size, unsigned long num, int base)
{
const char *sym = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char* sp;
sp = ptr;
size--; /* reserve one for \0 */
do {
*sp++ = sym[num % base];
num /= base;
} while (num > 0 && size-- > 0);
if(num > 0)
return 0;
*sp = '\0';
clc_strrev(ptr);
return 1;
}
I ran my test program with both versions. The clc_strrev version is 5%
faster on one of my machines than the original which uses memmove. ;-)
I think you should post the nice functions as well. Never know if
someone needs them or suddenly finds a new way of doing things. :-)
--
boa
Please join the libclc-developers list
at http://lists.sourceforge.net/lists/listinfo/libclc-developers
|
|
From: <bo...@me...> - 2003-03-23 12:16:54
|
Hallvard B Furuseth wrote: > A lot of libclc files will contain small functions, so I if we put a > huge copyright notice at the top of each, more than half of the released > libclc source text may be copyright notices... > > Can we get away with just this notice at the top of files? That's what > OpenLDAP does. > > Copyright <year> <Author> > COPYING RESTRICTIONS APPLY, see COPYRIGHT.txt file > I've been thinking exactly the same lately. We should be able to refer to an external file. That file should not be named COPYRIGHT.txt, but refer to a file containing our *license*. The license is currently available in libclc/COPYING. -- boa Please join the libclc-developers list at http://lists.sourceforge.net/lists/listinfo/libclc-developers |