@@ -285,10 +285,6 @@ void RedisReplyBuilder::SetResp3(bool is_resp3) {
285
285
is_resp3_ = is_resp3;
286
286
}
287
287
288
- bool RedisReplyBuilder::IsResp3 () const {
289
- return is_resp3_;
290
- }
291
-
292
288
void RedisReplyBuilder::SendError (string_view str, string_view err_type) {
293
289
VLOG (1 ) << " Error: " << str;
294
290
@@ -380,21 +376,33 @@ void RedisReplyBuilder::SendScoredArray(const std::vector<std::pair<std::string,
380
376
bool with_scores) {
381
377
ReplyAggregator agg (this );
382
378
if (!with_scores) {
383
- StartArray (arr.size ());
384
- for (const auto & p : arr) {
385
- SendBulkString (p.first );
386
- }
379
+ auto cb = [&](size_t indx) -> string_view { return arr[indx].first ; };
380
+
381
+ SendStringArrInternal (arr.size (), std::move (cb), CollectionType::ARRAY);
387
382
return ;
388
383
}
384
+
385
+ char buf[DoubleToStringConverter::kBase10MaximalLength * 3 ]; // to be on the safe side.
386
+
389
387
if (!is_resp3_) { // RESP2 formats withscores as a flat array.
390
- StartArray (arr.size () * 2 );
391
- for (const auto & p : arr) {
392
- SendBulkString (p.first );
393
- SendDouble (p.second );
394
- }
388
+ auto cb = [&](size_t indx) -> string_view {
389
+ if (indx % 2 == 0 )
390
+ return arr[indx / 2 ].first ;
391
+
392
+ // NOTE: we reuse the same buffer, assuming that SendStringArrInternal does not reference
393
+ // previous string_views. The assumption holds for small strings like
394
+ // doubles because SendStringArrInternal employs small string optimization.
395
+ // It's a bit hacky but saves allocations.
396
+ return FormatDouble (arr[indx / 2 ].second , buf, sizeof (buf));
397
+ };
398
+
399
+ SendStringArrInternal (arr.size () * 2 , std::move (cb), CollectionType::ARRAY);
395
400
return ;
396
401
}
402
+
397
403
// Resp3 formats withscores as array of (key, score) pairs.
404
+ // TODO: to implement efficient serializing by extending SendStringArrInternal to support
405
+ // 2-level arrays.
398
406
StartArray (arr.size ());
399
407
for (const auto & p : arr) {
400
408
StartArray (2 );
@@ -406,15 +414,13 @@ void RedisReplyBuilder::SendScoredArray(const std::vector<std::pair<std::string,
406
414
void RedisReplyBuilder::SendDouble (double val) {
407
415
char buf[64 ];
408
416
409
- StringBuilder sb (buf, sizeof (buf));
410
- CHECK (dfly_conv.ToShortest (val, &sb));
417
+ char * start = FormatDouble (val, buf, sizeof (buf));
411
418
412
419
if (!is_resp3_) {
413
- SendBulkString (sb. Finalize () );
420
+ SendBulkString (start );
414
421
} else {
415
422
// RESP3
416
- string str = absl::StrCat (" ," , sb.Finalize (), kCRLF );
417
- SendRaw (str);
423
+ SendRaw (absl::StrCat (" ," , start, kCRLF ));
418
424
}
419
425
}
420
426
@@ -576,10 +582,11 @@ void RedisReplyBuilder::SendStringArrInternal(
576
582
}
577
583
578
584
// When vector length is too long, Send returns EMSGSIZE.
579
- size_t vec_len = std::min<size_t >(128u , size);
585
+ size_t vec_len = std::min<size_t >(124u , size);
580
586
581
587
absl::FixedArray<iovec, 16 > vec (vec_len * 2 + 2 );
582
- absl::FixedArray<char , 64 > meta ((vec_len + 1 ) * 16 ); // 16 bytes per length.
588
+ absl::FixedArray<char , 128 > meta (vec_len * 32 + 64 ); // 32 bytes per element + spare space
589
+
583
590
char * next = meta.data ();
584
591
585
592
auto serialize_len = [&](char prefix, size_t len) {
@@ -598,18 +605,27 @@ void RedisReplyBuilder::SendStringArrInternal(
598
605
for (unsigned i = 0 ; i < size; ++i) {
599
606
src = producer (i);
600
607
serialize_len (' $' , src.size ());
608
+
601
609
// add serialized len blob
602
610
vec[vec_indx++] = IoVec (string_view{start, size_t (next - start)});
603
611
DCHECK_GT (next - start, 0 );
604
612
605
613
start = next;
606
614
607
- vec[vec_indx++] = IoVec (src);
608
-
615
+ // copy data either by referencing via an iovec or copying inline into meta buf.
616
+ if (src.size () >= 30 ) {
617
+ vec[vec_indx++] = IoVec (src);
618
+ } else if (src.size () > 0 ) {
619
+ memcpy (next, src.data (), src.size ());
620
+ vec[vec_indx - 1 ].iov_len += src.size (); // extend the reference
621
+ next += src.size ();
622
+ start = next;
623
+ }
609
624
*next++ = ' \r ' ;
610
625
*next++ = ' \n ' ;
611
626
612
- if (vec_indx + 1 >= vec.size ()) {
627
+ // we keep at least 40 bytes to have enough place for a small string as well as its length.
628
+ if (vec_indx + 1 >= vec.size () || (meta.end () - next < 40 )) {
613
629
// Flush the iovec array.
614
630
if (i < size - 1 || vec_indx == vec.size ()) {
615
631
Send (vec.data (), vec_indx);
0 commit comments