@@ -21,6 +21,13 @@ namespace dfly {
2121class ISLEntry {
2222 friend class IntrusiveStringList ;
2323
24+ // we can assume that high 12 bits of user address space
25+ // can be used for tagging. At most 52 bits of address are reserved for
26+ // some configurations, and usually it's 48 bits.
27+ // https://docs.kernel.org/arch/arm64/memory.html
28+ static constexpr size_t kTtlBit = 1ULL << 55 ;
29+ static constexpr size_t kTagMask = 4095ULL << 52 ; // we reserve 12 high bits.
30+
2431 public:
2532 ISLEntry () = default ;
2633
@@ -36,50 +43,95 @@ class ISLEntry {
3643 return {GetKeyData (), GetKeySize ()};
3744 }
3845
46+ bool HasExpiry () const {
47+ return HasTtl ();
48+ }
49+
50+ // returns the expiry time of the current entry or UINT32_MAX if no ttl is set.
51+ uint32_t ExpiryTime () const {
52+ std::uint32_t res = UINT32_MAX;
53+ if (HasTtl ()) {
54+ std::memcpy (&res, Raw () + sizeof (ISLEntry*), sizeof (res));
55+ }
56+ return res;
57+ }
58+
3959 private:
40- static ISLEntry Create (std::string_view key) {
60+ static ISLEntry Create (std::string_view key, uint32_t ttl_sec = UINT32_MAX ) {
4161 char * next = nullptr ;
4262 uint32_t key_size = key.size ();
4363
44- auto size = sizeof (next) + sizeof (key_size) + key_size;
64+ bool has_ttl = ttl_sec != UINT32_MAX;
65+
66+ auto size = sizeof (next) + sizeof (key_size) + key_size + has_ttl * sizeof (ttl_sec);
4567
4668 char * data = (char *)malloc (size);
69+ ISLEntry res (data);
4770
4871 std::memcpy (data, &next, sizeof (next));
4972
50- auto * key_size_pos = data + sizeof (next);
73+ auto * ttl_pos = data + sizeof (next);
74+ if (has_ttl) {
75+ res.SetTtlBit (true );
76+ std::memcpy (ttl_pos, &ttl_sec, sizeof (ttl_sec));
77+ }
78+
79+ auto * key_size_pos = ttl_pos + res.GetTtlSize ();
5180 std::memcpy (key_size_pos, &key_size, sizeof (key_size));
5281
5382 auto * key_pos = key_size_pos + sizeof (key_size);
5483 std::memcpy (key_pos, key.data (), key_size);
5584
56- return ISLEntry (data) ;
85+ return res ;
5786 }
5887
5988 static void Destroy (ISLEntry entry) {
60- free (entry.data_ );
89+ free (entry.Raw () );
6190 }
6291
6392 ISLEntry Next () const {
6493 ISLEntry next;
65- std::memcpy (&next.data_ , data_ , sizeof (next));
94+ std::memcpy (&next.data_ , Raw () , sizeof (next));
6695 return next;
6796 }
6897
6998 void SetNext (ISLEntry next) {
70- std::memcpy (data_ , &next, sizeof (next));
99+ std::memcpy (Raw () , &next, sizeof (next));
71100 }
72101
73102 const char * GetKeyData () const {
74- return data_ + sizeof (ISLEntry*) + sizeof (uint32_t );
103+ return Raw () + sizeof (ISLEntry*) + sizeof (uint32_t ) + GetTtlSize ( );
75104 }
76105
77106 uint32_t GetKeySize () const {
78107 uint32_t size = 0 ;
79- std::memcpy (&size, data_ + sizeof (ISLEntry*), sizeof (size));
108+ std::memcpy (&size, Raw () + sizeof (ISLEntry*) + GetTtlSize ( ), sizeof (size));
80109 return size;
81110 }
82111
112+ uint64_t uptr () const {
113+ return uint64_t (data_);
114+ }
115+
116+ char * Raw () const {
117+ return (char *)(uptr () & ~kTagMask );
118+ }
119+
120+ void SetTtlBit (bool b) {
121+ if (b)
122+ data_ = (char *)(uptr () | kTtlBit );
123+ else
124+ data_ = (char *)(uptr () & (~kTtlBit ));
125+ }
126+
127+ bool HasTtl () const {
128+ return (uptr () & kTtlBit ) != 0 ;
129+ }
130+
131+ std::uint32_t GetTtlSize () const {
132+ return HasTtl () ? sizeof (std::uint32_t ) : 0 ;
133+ }
134+
83135 // TODO consider use SDS strings or other approach
84136 // TODO add optimization for big keys
85137 // memory daya layout [ISLEntry*, key_size, key]
@@ -117,8 +169,8 @@ class IntrusiveStringList {
117169 return res;
118170 }
119171
120- ISLEntry Emplace (std::string_view key) {
121- return Insert (ISLEntry::Create (key));
172+ ISLEntry Emplace (std::string_view key, uint32_t ttl_sec = UINT32_MAX ) {
173+ return Insert (ISLEntry::Create (key, ttl_sec ));
122174 }
123175
124176 ISLEntry Find (std::string_view str) const {
@@ -189,7 +241,7 @@ class IntrusiveStringSet {
189241 ISLEntry AddUnique (std::string_view str, IntrusiveStringList& bucket,
190242 uint32_t ttl_sec = UINT32_MAX) {
191243 ++size_;
192- return bucket.Emplace (str);
244+ return bucket.Emplace (str, ttl_sec );
193245 }
194246
195247 unsigned AddMany (absl::Span<std::string_view> span, uint32_t ttl_sec, bool keepttl) {
@@ -219,7 +271,12 @@ class IntrusiveStringSet {
219271 if (entries_.empty ())
220272 return {};
221273 auto bucket_id = BucketId (Hash (member));
222- return entries_[bucket_id].Find (member);
274+ auto res = entries_[bucket_id].Find (member);
275+ if (!res) {
276+ bucket_id = BucketId (Hash (member));
277+ res = entries_[bucket_id].Find (member);
278+ }
279+ return res;
223280 }
224281
225282 bool Contains (std::string_view member) const {
@@ -240,6 +297,15 @@ class IntrusiveStringSet {
240297 return 1 << capacity_log_;
241298 }
242299
300+ // set an abstract time that allows expiry.
301+ void set_time (uint32_t val) {
302+ time_now_ = val;
303+ }
304+
305+ uint32_t time_now () const {
306+ return time_now_;
307+ }
308+
243309 private:
244310 // was Grow in StringSet
245311 void Rehash (size_t prev_size) {
@@ -265,6 +331,7 @@ class IntrusiveStringSet {
265331 private:
266332 std::uint32_t capacity_log_ = 1 ;
267333 std::uint32_t size_ = 0 ; // number of elements in the set.
334+ std::uint32_t time_now_ = 0 ;
268335
269336 static_assert (sizeof (IntrusiveStringList) == sizeof (void *),
270337 " IntrusiveStringList should be just a pointer" );
0 commit comments