/*
 *	sds.c
 *
 *	Simple Dynamic Strings library for C (SDS)
 *
 *	* Fri Sep 18 21:22:35 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	- 「Simple Dynamic Strings library for C」(https://github.com/antirez/sds)互換モジュールです。
 *	  関数仕様はSDSと同じで、実装は独自です。
 *	  オリジナル版のSDSは速度性能を重視して実装されているようですが、当実装は省メモリを優先して単純に実装しました。
 *	- 関数仕様や使用例は、オリジナル版のSDSのドキュメントを参照してください。
 *	  オリジナル版のSDS verison 2を、/clip/keep/sds-master.7zに保存しておきました。
 *	- 各関数のコメントは、オリジナル版のSDSのコメントをそのまま残させて頂きました。
 *	  ただし、当実装の特性とは、必ずしも一致していない事に注意してください。
 */
#include "clip.h"
//==============================================================================
//	
//==============================================================================
size_t sdslen(sds s) {
	return strlen(s);
}
//------------------------------------------------------------------------------
sds sdsnewlen(const void* init, size_t initlen) {
#ifndef GC_LEAK_DETECTOR_H
	char* s = strndup(       init     , initlen);				//(init=NULL,initlen=0)の場合に、自作のstrndup()ならば動作するが、Boehm GCのstrndup()ではエラーになる。
#else //GC_LEAK_DETECTOR_H
	char* s = strndup(init ? init : "", initlen);				//問題を回避するために、Boehm GCのleak_detector.hをインクルードした場合は、こちらを使用するようにした。
#endif//GC_LEAK_DETECTOR_H
	if(!s) { DIE(); }
	return s;
}
//------------------------------------------------------------------------------
//Create a new sds string starting from a null terminated C string.
sds sdsnew(const char* init) {
	char* s = strdup(init);
	if(!s) { DIE(); }
	return s;
}
//------------------------------------------------------------------------------
//Create an empty (zero length) sds string.
//Even in this case the string always has an implicit null term.
sds sdsempty() {
	return sdsnewlen(NULL, 0);
}
//------------------------------------------------------------------------------
//Duplicate an sds string.
sds sdsdup(sds s) {
	return sdsnew(s);
}
//------------------------------------------------------------------------------
//Free an sds string. No operation is performed if 's' is NULL.
void sdsfree(sds s) {
	free(s);
}
//------------------------------------------------------------------------------
//Append the specified binary-safe string pointed by 't' of 'len' bytes to the end of the specified sds string 's'.
//After the call, the passed sds string is no longer valid and all the references must be substituted with the new pointer returned by the call.
sds sdscatlen(sds s, const void* t, size_t len) {				//エリアスセーフです。(s=t)でも大丈夫です。
	char* x = sdsnewlen(t, len);
	s = sdscat(s, x);
	free(x);
	return s;
}
//------------------------------------------------------------------------------
//Append the specified null termianted C string to the sds string 's'.
//After the call, the passed sds string is no longer valid and all the references must be substituted with the new pointer returned by the call.
sds sdscat(sds s, const char* t) {						//エリアスセーフです。(s=t)でも大丈夫です。
	char* x = strconcat(s, t, NULL);
	free(s);
	return x;
}
//------------------------------------------------------------------------------
//Append the specified sds 't' to the existing sds 's'.
//After the call, the modified sds string is no longer valid and all the references must be substituted with the new pointer returned by the call.
sds sdscatsds(sds s, sds t) {							//エリアスセーフです。(s=t)でも大丈夫です。
	return sdscat(s, t);
}
//------------------------------------------------------------------------------
//Destructively modify the sds string 's' to hold the specified binary safe string pointed by 't' of length 'len' bytes.
sds sdscpylen(sds s, const char* t, size_t len) {				//エリアスセーフです。(s=t)でも大丈夫です。
	char* x = sdsnewlen(t, len);
	free(s);
	return x;
}
//------------------------------------------------------------------------------
//Like sdscpylen() but 't' must be a null-termined string so that the length of the string is obtained with strlen().
sds sdscpy(sds s, const char* t) {						//エリアスセーフです。(s=t)でも大丈夫です。
	char* x = sdsnew(t);
	free(s);
	return x;
}
//------------------------------------------------------------------------------
//Like sdscatprintf() but gets va_list instead of being variadic.
sds sdscatvprintf(sds s, const char* fmt, va_list ap) {
	char* x = strdup_vprintf(fmt, ap);
	s = sdscat(s, x);
	free(x);
	return s;
}
//------------------------------------------------------------------------------
//Append to the sds string 's' a string obtained using printf-alike format specifier.
//After the call, the modified sds string is no longer valid and all the references must be substituted with the new pointer returned by the call.
//Example:
//│s = sdsnew("Sum is: ");
//│s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
//Often you need to create a string from scratch with the printf-alike format.
//When this is the need, just use sdsempty() as the target string:
//│s = sdscatprintf(sdsempty(), "... your format ...", args);
sds sdscatprintf(sds s, const char* fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	s = sdscatvprintf(s, fmt, ap);
	va_end(ap);
	return s;
}
//------------------------------------------------------------------------------
//Remove the part of the string from left and from right composed just of contiguous characters found in 'cset', that is a null terminted C string.
//After the call, the modified sds string is no longer valid and all the references must be substituted with the new pointer returned by the call.
//Example:
//│s = sdsnew("AA...AA.a.aa.aHelloWorld     :::");
//│s = sdstrim(s,"Aa. :");
//│printf("%s\n",s);
//Output will be just "Hello World".
sds sdstrim(sds s, const char* cset) {
	char* x = strtrim(s, 0, cset);
	free(s);
	return x;
}
//------------------------------------------------------------------------------
//Turn the string into a smaller (or equal) string containing only the substring specified by the 'start' and 'end' indexes.
//Start and end can be negative, where -1 means the last character of the string, -2 the penultimate character, and so forth.
//The interval is inclusive, so the start and end characters will be part of the resulting string.
//The string is modified in-place.
//Example:
//│s = sdsnew("Hello World");
//│sdsrange(s,1,-1); => "ello World"
void sdsrange(sds s, int start, int end) {					//'end'が示す位置の文字も含む事に注意せよ。
	int len = sdslen(s), newlen = 0;
	if(start < 0) { start += len; }
	if(end   < 0) { end   += len; }
	if((start < len) && (end >= 0) && (start <= end)) {
		newlen = end - start + 1;
		memmove(s, s + start, newlen);
	}
	s[newlen] = '\0';
}
//------------------------------------------------------------------------------
//Set the sds string length to the length as obtained with strlen(), so considering as content only up to the first null term character.
//This function is useful when the sds string is hacked manually in some way, like in the following example:
//│s = sdsnew("foobar");
//│s[2] = '\0';
//│sdsupdatelen(s);
//│printf("%d\n", sdslen(s));
//The output will be "2", but if we comment out the call to sdsupdatelen() the output will be "6" as the string was modified but the logical length remains 6 bytes.
void sdsupdatelen(sds s) {
	/** no job **/
}
//------------------------------------------------------------------------------
//Modify an sds string in-place to make it empty (zero length).
//However all the existing buffer is not discarded but set as free space so that next append operations will not require allocations up to the number of bytes previously available.
void sdsclear(sds s) {
	s[0] = '\0';
}
//------------------------------------------------------------------------------
//Compare two sds strings s1 and s2 with memcmp().
//Return value:
//│positive if s1 > s2.
//│negative if s1 < s2.
//│0 if s1 and s2 are exactly the same binary string.
//If two strings share exactly the same prefix, but one of the two has additional characters, the longer string is considered to be greater than the smaller one.
int sdscmp(sds s1, sds s2) {
	return strcmp(s1, s2);
}
//------------------------------------------------------------------------------
//Split 's' with separator in 'sep'.
//An array of sds strings is returned.
//*count will be set by reference to the number of tokens returned.
//On out of memory, zero length string, zero length separator, NULL is returned.
//Note that 'sep' is able to split a string using a multi-character separator.
//For example sdssplit("foo_-_bar","_-_"); will return two elements "foo" and "bar".
//This version of the function is binary-safe but requires length arguments.
//sdssplit() is just the same function but for zero-terminated strings.
sds* sdssplitlen(const char* s, int len, const char* sep, int seplen, int* count) {
	sds* x;
	if(!(s   = strndup(s,   len   ))) { DIE(); }
	if(!(sep = strndup(sep, seplen))) { DIE(); }
	x = strsplit(s, sep, 0);
	*count = strv_length(x);
	free((void*)s  );
	free((void*)sep);
	return x;
}
//------------------------------------------------------------------------------
//Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL.
void sdsfreesplitres(sds* tokens, int count) {
	if(tokens) {
		while(count--) { free(tokens[count]); }
		free(tokens);
	}
}
//------------------------------------------------------------------------------
//Apply tolower() to every character of the sds string 's'.
void sdstolower(sds s) {
	strlwr(s);
}
//------------------------------------------------------------------------------
//Apply toupper() to every character of the sds string 's'.
void sdstoupper(sds s) {
	strupr(s);
}
//------------------------------------------------------------------------------
//Create an sds string from a long long value.
//It is much faster than:
//│sdscatprintf(sdsempty(),"%lld\n",value);
sds sdsfromlonglong(int64_t value) {
	return sdscatprintf(sdsempty(), "%lld", value);
}
//------------------------------------------------------------------------------
//Append to the sds string "s" an escaped string representation where all the non-printable characters (tested with isprint()) are turned into escapes in the form "\n\r\a...." or "\x<hex-number>".
//After the call, the modified sds string is no longer valid and all the references must be substituted with the new pointer returned by the call.
sds sdscatrepr(sds s, const char* p, size_t len) {				//エリアスセーフです。(s=p)でも大丈夫です。
	sds x = sdsnewlen(p, len);
	sds y = strescape(x, NULL);
	s = sdscat(s, "\"");
	s = sdscat(s, y);
	s = sdscat(s, "\"");
	free(x);
	free(y);
	return s;
}
//------------------------------------------------------------------------------
//Modify the string substituting all the occurrences of the set of characters specified in the 'from' string to the corresponding character in the 'to' array.
//For instance: sdsmapchars(mystring, "ho", "01", 2) will have the effect of turning the string "hello" into "0ell1".
//The function returns the sds string pointer, that is always the same as the input pointer since no resize is needed.
sds sdsmapchars(sds s, const char* from, const char* to, size_t setlen) {
	char* x = s;
	while(*x) {
		int i;
		for(i = 0; i < (int)setlen; i++) {
			if(*x == from[i]) { *x = to[i]; break; }
		}
		x++;
	}
	return s;
}
//------------------------------------------------------------------------------
//Join an array of C strings using the specified separator (also a C string).
//Returns the result as an sds string.
sds sdsjoin(char** argv, int argc, const char* sep) {
	char *x, **y;
	if(!(y = calloc(argc + 1/*NULL*/, sizeof(char*)))) { DIE(); }
	memcpy(y, argv, sizeof(char*) * argc);
	x = strjoinv(sep, y);
	free(y);
	return x;
}
//------------------------------------------------------------------------------
//Like sdsjoin, but joins an array of SDS strings.
sds sdsjoinsds(sds* argv, int argc, const char* sep, size_t seplen) {
	char* x;
	sep = sdsnewlen(sep, seplen);
	x = sdsjoin(argv, argc, sep);
	free((void*)sep);
	return x;
}
//==============================================================================
//	Low level functions exposed to the user API
//==============================================================================
sds sdsMakeRoomFor(sds s, size_t addlen) {
	if(!(s = realloc(s, sdslen(s) + addlen + 1/*nul*/))) { DIE(); }
	return s;
}
//------------------------------------------------------------------------------
void sdsIncrLen(sds s, int incr) {
	/** no job **/
}
//------------------------------------------------------------------------------
sds sdsRemoveFreeSpace(sds s) {
	return sdsMakeRoomFor(s, 0);
}
