From 20e529c441c3948ff6fc720cf9d4cf763f2e2647 Mon Sep 17 00:00:00 2001 From: John Sully Date: Wed, 1 Jan 2020 19:13:48 -0500 Subject: [PATCH] C++ wrapper classes for SDS Former-commit-id: 45817db8c3a86815945359113dcbccfde4257ce5 --- src/sds.c | 43 +++++++++++++++-- src/sds.h | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 176 insertions(+), 5 deletions(-) diff --git a/src/sds.c b/src/sds.c index 637db9663..e1678a95b 100644 --- a/src/sds.c +++ b/src/sds.c @@ -53,11 +53,18 @@ static inline int sdsHdrSize(char type) { return sizeof(struct sdshdr32); case SDS_TYPE_64: return sizeof(struct sdshdr64); + case SDS_TYPE_REFCOUNTED: + return sizeof(struct sdshdrrefcount); } return 0; } -static inline char sdsReqType(size_t string_size) { +static inline char sdsReqType(ssize_t string_size) { + if (string_size < 0){ + string_size = -string_size; + if (string_size < 1<<16) + return SDS_TYPE_REFCOUNTED; + } if (string_size < 1<<5) return SDS_TYPE_5; if (string_size < 1<<8) @@ -86,10 +93,12 @@ static inline char sdsReqType(size_t string_size) { * You can print the string with printf() as there is an implicit \0 at the * end of the string. However the string is binary safe and can contain * \0 characters in the middle, as the length is stored in the sds header. */ -sds sdsnewlen(const void *init, size_t initlen) { +sds sdsnewlen(const void *init, ssize_t initlen) { void *sh; sds s; char type = sdsReqType(initlen); + if (initlen < 0) + initlen = -initlen; /* Empty strings are usually created in order to append. Use type 8 * since type 5 is not good at this. */ if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; @@ -137,6 +146,13 @@ sds sdsnewlen(const void *init, size_t initlen) { *fp = type; break; } + case SDS_TYPE_REFCOUNTED: { + SDS_HDR_VAR_REFCOUNTED(s); + sh->len = initlen; + sh->refcount = 1; + *fp = type; + break; + } } if (initlen && init) memcpy(s, init, initlen); @@ -161,9 +177,25 @@ sds sdsdup(const char *s) { return sdsnewlen(s, sdslen(s)); } +sds sdsdupshared(const char *s) { + unsigned char flags = s[-1]; + if ((flags & SDS_TYPE_MASK) != SDS_TYPE_REFCOUNTED) + return sdsnewlen(s, -sdslen(s)); + SDS_HDR_VAR_REFCOUNTED(s); + __atomic_fetch_add(&sh->refcount, 1, __ATOMIC_RELAXED); + return (sds)s; +} + /* Free an sds string. No operation is performed if 's' is NULL. */ void sdsfree(const char *s) { if (s == NULL) return; + unsigned char flags = s[-1]; + if ((flags & SDS_TYPE_MASK) == SDS_TYPE_REFCOUNTED) + { + SDS_HDR_VAR_REFCOUNTED(s); + if (__atomic_fetch_sub(&sh->refcount, 1, __ATOMIC_RELAXED) > 1) + return; + } s_free((char*)s-sdsHdrSize(s[-1])); } @@ -368,6 +400,11 @@ void sdsIncrLen(sds s, ssize_t incr) { len = (sh->len += incr); break; } + case SDS_TYPE_REFCOUNTED: { + SDS_HDR_VAR_REFCOUNTED(s); + len = (sh->len += incr); + break; + } default: len = 0; /* Just to avoid compilation warnings. */ } s[len] = '\0'; @@ -787,7 +824,7 @@ void sdstoupper(sds s) { * 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(const sds s1, const sds s2) { +int sdscmp(const char *s1, const char *s2) { size_t l1, l2, minlen; int cmp; diff --git a/src/sds.h b/src/sds.h index 7f6f141e0..23a11afa4 100644 --- a/src/sds.h +++ b/src/sds.h @@ -91,15 +91,27 @@ struct __attribute__ ((__packed__)) sdshdr64 { #endif }; +struct __attribute__ ((__packed__)) sdshdrrefcount { + uint64_t len; /* used */ + uint16_t refcount; + unsigned char flags; /* 3 lsb of type, 5 unused bits */ +#ifndef __cplusplus + char buf[]; +#endif +}; + #define SDS_TYPE_5 0 #define SDS_TYPE_8 1 #define SDS_TYPE_16 2 #define SDS_TYPE_32 3 #define SDS_TYPE_64 4 +#define SDS_TYPE_REFCOUNTED 5 #define SDS_TYPE_MASK 7 #define SDS_TYPE_BITS 3 #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)(((void*)((s)-(sizeof(struct sdshdr##T))))); +#define SDS_HDR_VAR_REFCOUNTED(s) struct sdshdrrefcount *sh = (struct sdshdrrefcount *)(((void*)((s)-(sizeof(struct sdshdrrefcount))))); #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) +#define SDS_HDR_REFCOUNTED(s) ((struct sdshdrrefcount *)((s)-(sizeof(struct sdshdrrefcount)))) #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) static inline size_t sdslen(const char *s) { @@ -121,6 +133,8 @@ static inline size_t sdslen(const char *s) { return SDS_HDR(32,s)->len; case SDS_TYPE_64: return SDS_HDR(64,s)->len; + case SDS_TYPE_REFCOUNTED: + return SDS_HDR_REFCOUNTED(s)->len; } } return 0; @@ -148,6 +162,9 @@ static inline size_t sdsavail(const char * s) { SDS_HDR_VAR(64,s); return sh->alloc - sh->len; } + case SDS_TYPE_REFCOUNTED: { + return 0; // immutable + } } return 0; } @@ -173,6 +190,9 @@ static inline void sdssetlen(sds s, size_t newlen) { case SDS_TYPE_64: SDS_HDR(64,s)->len = newlen; break; + case SDS_TYPE_REFCOUNTED: + SDS_HDR_REFCOUNTED(s)->len = newlen; + break; } } @@ -198,6 +218,9 @@ static inline void sdsinclen(sds s, size_t inc) { case SDS_TYPE_64: SDS_HDR(64,s)->len += inc; break; + case SDS_TYPE_REFCOUNTED: + SDS_HDR_REFCOUNTED(s)->len += inc; + break; } } @@ -215,6 +238,8 @@ static inline size_t sdsalloc(const sds s) { return SDS_HDR(32,s)->alloc; case SDS_TYPE_64: return SDS_HDR(64,s)->alloc; + case SDS_TYPE_REFCOUNTED: + return SDS_HDR_REFCOUNTED(s)->len; } return 0; } @@ -237,13 +262,22 @@ static inline void sdssetalloc(sds s, size_t newlen) { case SDS_TYPE_64: SDS_HDR(64,s)->alloc = newlen; break; + case SDS_TYPE_REFCOUNTED: + break; } } -sds sdsnewlen(const void *init, size_t initlen); +static inline int sdsisshared(const char *s) +{ + unsigned char flags = s[-1]; + return ((flags & SDS_TYPE_MASK) == SDS_TYPE_REFCOUNTED); +} + +sds sdsnewlen(const void *init, ssize_t initlen); sds sdsnew(const char *init); sds sdsempty(void); sds sdsdup(const char *s); +sds sdsdupshared(const char *s); void sdsfree(const char *s); sds sdsgrowzero(sds s, size_t len); sds sdscatlen(sds s, const void *t, size_t len); @@ -265,7 +299,7 @@ sds sdstrim(sds s, const char *cset); void sdsrange(sds s, ssize_t start, ssize_t end); void sdsupdatelen(sds s); void sdsclear(sds s); -int sdscmp(const sds s1, const sds s2); +int sdscmp(const char *s1, const char *s2); sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count); void sdsfreesplitres(sds *tokens, int count); void sdstolower(sds s); @@ -298,6 +332,106 @@ int sdsTest(int argc, char *argv[]); #ifdef __cplusplus } + +class sdsview +{ +protected: + sds m_str = nullptr; + + sdsview() = default; // Not allowed to create a sdsview directly with a nullptr +public: + sdsview(sds str) + : m_str(str) + {} + + sdsview(const char *str) + : m_str((sds)str) + {} + + bool operator<(const sdsview &other) const + { + return sdscmp(m_str, other.m_str) < 0; + } + + bool operator==(const sdsview &other) const + { + return sdscmp(m_str, other.m_str) == 0; + } + + bool operator==(const char *other) const + { + return sdscmp(m_str, other) == 0; + } + + char operator[](size_t idx) const + { + return m_str[idx]; + } + + size_t size() const + { + return sdslen(m_str); + } + + const char *get() const { return m_str; } + + explicit operator const char*() const { return m_str; } +}; + +class sdsstring : public sdsview +{ +public: + sdsstring() = default; + explicit sdsstring(sds str) + : sdsview(str) + {} + + sdsstring(const sdsstring &other) + : sdsview(sdsdup(other.m_str)) + {} + + sdsstring(sdsstring &&other) + : sdsview(other.m_str) + { + other.m_str = nullptr; + } + + ~sdsstring() + { + sdsfree(m_str); + } +}; + +class sdsimmutablestring : public sdsstring +{ +public: + sdsimmutablestring() = default; + explicit sdsimmutablestring(sds str) + : sdsstring(str) + {} + + explicit sdsimmutablestring(const char *str) + : sdsstring((sds)str) + {} + + sdsimmutablestring(const sdsimmutablestring &other) + : sdsstring(sdsdupshared(other.m_str)) + {} + + sdsimmutablestring(sdsimmutablestring &&other) + : sdsstring(other.m_str) + { + other.m_str = nullptr; + } + + auto &operator=(const sdsimmutablestring &other) + { + sdsfree(m_str); + m_str = sdsdupshared(other.m_str); + return *this; + } +}; + #endif #endif