Coverage Report

Created: 2024-09-03 06:23

/src/brpc/src/brpc/memcache.cpp
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
19
#include <algorithm>
20
#include <google/protobuf/reflection_ops.h>
21
#include <google/protobuf/wire_format.h>
22
#include "butil/string_printf.h"
23
#include "butil/macros.h"
24
#include "butil/sys_byteorder.h"
25
#include "butil/logging.h"
26
#include "brpc/memcache.h"
27
#include "brpc/policy/memcache_binary_header.h"
28
29
namespace brpc {
30
31
MemcacheRequest::MemcacheRequest()
32
0
    : ::google::protobuf::Message() {
33
0
    SharedCtor();
34
0
}
35
36
MemcacheRequest::MemcacheRequest(const MemcacheRequest& from)
37
0
    : ::google::protobuf::Message() {
38
0
    SharedCtor();
39
0
    MergeFrom(from);
40
0
}
41
42
0
void MemcacheRequest::SharedCtor() {
43
0
    _pipelined_count = 0;
44
0
    _cached_size_ = 0;
45
0
}
46
47
0
MemcacheRequest::~MemcacheRequest() {
48
0
    SharedDtor();
49
0
}
50
51
0
void MemcacheRequest::SharedDtor() {
52
0
}
53
54
0
void MemcacheRequest::SetCachedSize(int size) const {
55
0
    _cached_size_ = size;
56
0
}
57
58
0
const ::google::protobuf::Descriptor* MemcacheRequest::descriptor() {
59
0
    return MemcacheRequestBase::descriptor();
60
0
}
61
62
0
MemcacheRequest* MemcacheRequest::New() const {
63
0
    return new MemcacheRequest;
64
0
}
65
66
#if GOOGLE_PROTOBUF_VERSION >= 3006000
67
0
MemcacheRequest* MemcacheRequest::New(::google::protobuf::Arena* arena) const {
68
0
    return CreateMaybeMessage<MemcacheRequest>(arena);
69
0
}
70
#endif
71
72
0
void MemcacheRequest::Clear() {
73
0
    _buf.clear();
74
0
    _pipelined_count = 0;
75
0
}
76
77
bool MemcacheRequest::MergePartialFromCodedStream(
78
0
    ::google::protobuf::io::CodedInputStream* input) {
79
0
    LOG(WARNING) << "You're not supposed to parse a MemcacheRequest";
80
    
81
    // simple approach just making it work.
82
0
    butil::IOBuf tmp;
83
0
    const void* data = NULL;
84
0
    int size = 0;
85
0
    while (input->GetDirectBufferPointer(&data, &size)) {
86
0
        tmp.append(data, size);
87
0
        input->Skip(size);
88
0
    }
89
0
    const butil::IOBuf saved = tmp;
90
0
    int count = 0;
91
0
    for (; !tmp.empty(); ++count) {
92
0
        char aux_buf[sizeof(policy::MemcacheRequestHeader)];
93
0
        const policy::MemcacheRequestHeader* header =
94
0
            (const policy::MemcacheRequestHeader*)tmp.fetch(aux_buf, sizeof(aux_buf));
95
0
        if (header == NULL) {
96
0
            return false;
97
0
        }
98
0
        if (header->magic != (uint8_t)policy::MC_MAGIC_REQUEST) {
99
0
            return false;
100
0
        }
101
0
        uint32_t total_body_length = butil::NetToHost32(header->total_body_length);
102
0
        if (tmp.size() < sizeof(*header) + total_body_length) {
103
0
            return false;
104
0
        }
105
0
        tmp.pop_front(sizeof(*header) + total_body_length);
106
0
    }
107
0
    _buf.append(saved);
108
0
    _pipelined_count += count;
109
0
    return true;
110
0
}
111
112
void MemcacheRequest::SerializeWithCachedSizes(
113
0
    ::google::protobuf::io::CodedOutputStream* output) const {
114
0
    LOG(WARNING) << "You're not supposed to serialize a MemcacheRequest";
115
116
    // simple approach just making it work.
117
0
    butil::IOBufAsZeroCopyInputStream wrapper(_buf);
118
0
    const void* data = NULL;
119
0
    int size = 0;
120
0
    while (wrapper.Next(&data, &size)) {
121
0
        output->WriteRaw(data, size);
122
0
    }
123
0
}
124
125
::google::protobuf::uint8* MemcacheRequest::SerializeWithCachedSizesToArray(
126
0
    ::google::protobuf::uint8* target) const {
127
0
    return target;
128
0
}
129
130
0
int MemcacheRequest::ByteSize() const {
131
0
    int total_size =  _buf.size();
132
0
    _cached_size_ = total_size;
133
0
    return total_size;
134
0
}
135
136
0
void MemcacheRequest::MergeFrom(const ::google::protobuf::Message& from) {
137
0
    CHECK_NE(&from, this);
138
0
    const MemcacheRequest* source = dynamic_cast<const MemcacheRequest*>(&from);
139
0
    if (source == NULL) {
140
0
        ::google::protobuf::internal::ReflectionOps::Merge(from, this);
141
0
    } else {
142
0
        MergeFrom(*source);
143
0
    }
144
0
}
145
146
0
void MemcacheRequest::MergeFrom(const MemcacheRequest& from) {
147
0
    CHECK_NE(&from, this);
148
0
    _buf.append(from._buf);
149
0
    _pipelined_count += from._pipelined_count;
150
0
}
151
152
0
void MemcacheRequest::CopyFrom(const ::google::protobuf::Message& from) {
153
0
    if (&from == this) return;
154
0
    Clear();
155
0
    MergeFrom(from);
156
0
}
157
158
0
void MemcacheRequest::CopyFrom(const MemcacheRequest& from) {
159
0
    if (&from == this) return;
160
0
    Clear();
161
0
    MergeFrom(from);
162
0
}
163
164
0
bool MemcacheRequest::IsInitialized() const {
165
0
    return _pipelined_count != 0;
166
0
}
167
168
0
void MemcacheRequest::Swap(MemcacheRequest* other) {
169
0
    if (other != this) {
170
0
        _buf.swap(other->_buf);
171
0
        std::swap(_pipelined_count, other->_pipelined_count);
172
0
        std::swap(_cached_size_, other->_cached_size_);
173
0
    }
174
0
}
175
176
0
::google::protobuf::Metadata MemcacheRequest::GetMetadata() const {
177
0
    ::google::protobuf::Metadata metadata;
178
0
    metadata.descriptor = MemcacheRequest::descriptor();
179
0
    metadata.reflection = NULL;
180
0
    return metadata;
181
0
}
182
183
MemcacheResponse::MemcacheResponse()
184
0
    : ::google::protobuf::Message() {
185
0
    SharedCtor();
186
0
}
187
188
MemcacheResponse::MemcacheResponse(const MemcacheResponse& from)
189
0
    : ::google::protobuf::Message() {
190
0
    SharedCtor();
191
0
    MergeFrom(from);
192
0
}
193
194
0
void MemcacheResponse::SharedCtor() {
195
0
    _cached_size_ = 0;
196
0
}
197
198
0
MemcacheResponse::~MemcacheResponse() {
199
0
    SharedDtor();
200
0
}
201
202
0
void MemcacheResponse::SharedDtor() {
203
0
}
204
205
0
void MemcacheResponse::SetCachedSize(int size) const {
206
0
    _cached_size_ = size;
207
0
}
208
0
const ::google::protobuf::Descriptor* MemcacheResponse::descriptor() {
209
0
    return MemcacheResponseBase::descriptor();
210
0
}
211
212
0
MemcacheResponse* MemcacheResponse::New() const {
213
0
    return new MemcacheResponse;
214
0
}
215
216
#if GOOGLE_PROTOBUF_VERSION >= 3006000
217
MemcacheResponse*
218
0
MemcacheResponse::New(::google::protobuf::Arena* arena) const {
219
0
    return CreateMaybeMessage<MemcacheResponse>(arena);
220
0
}
221
#endif
222
223
0
void MemcacheResponse::Clear() {
224
0
}
225
226
bool MemcacheResponse::MergePartialFromCodedStream(
227
0
    ::google::protobuf::io::CodedInputStream* input) {
228
0
    LOG(WARNING) << "You're not supposed to parse a MemcacheResponse";
229
230
    // simple approach just making it work.
231
0
    const void* data = NULL;
232
0
    int size = 0;
233
0
    while (input->GetDirectBufferPointer(&data, &size)) {
234
0
        _buf.append(data, size);
235
0
        input->Skip(size);
236
0
    }
237
0
    return true;
238
0
}
239
240
void MemcacheResponse::SerializeWithCachedSizes(
241
0
    ::google::protobuf::io::CodedOutputStream* output) const {
242
0
    LOG(WARNING) << "You're not supposed to serialize a MemcacheResponse";
243
    
244
    // simple approach just making it work.
245
0
    butil::IOBufAsZeroCopyInputStream wrapper(_buf);
246
0
    const void* data = NULL;
247
0
    int size = 0;
248
0
    while (wrapper.Next(&data, &size)) {
249
0
        output->WriteRaw(data, size);
250
0
    }
251
0
}
252
253
::google::protobuf::uint8* MemcacheResponse::SerializeWithCachedSizesToArray(
254
0
    ::google::protobuf::uint8* target) const {
255
0
    return target;
256
0
}
257
258
0
int MemcacheResponse::ByteSize() const {
259
0
    int total_size = _buf.size();
260
0
    _cached_size_ = total_size;
261
0
    return total_size;
262
0
}
263
264
0
void MemcacheResponse::MergeFrom(const ::google::protobuf::Message& from) {
265
0
    CHECK_NE(&from, this);
266
0
    const MemcacheResponse* source = dynamic_cast<const MemcacheResponse*>(&from);
267
0
    if (source == NULL) {
268
0
        ::google::protobuf::internal::ReflectionOps::Merge(from, this);
269
0
    } else {
270
0
        MergeFrom(*source);
271
0
    }
272
0
}
273
274
0
void MemcacheResponse::MergeFrom(const MemcacheResponse& from) {
275
0
    CHECK_NE(&from, this);
276
0
    _err = from._err;
277
    // responses of memcached according to their binary layout, should be
278
    // directly concatenatible.
279
0
    _buf.append(from._buf);
280
0
}
281
282
0
void MemcacheResponse::CopyFrom(const ::google::protobuf::Message& from) {
283
0
    if (&from == this) return;
284
0
    Clear();
285
0
    MergeFrom(from);
286
0
}
287
288
0
void MemcacheResponse::CopyFrom(const MemcacheResponse& from) {
289
0
    if (&from == this) return;
290
0
    Clear();
291
0
    MergeFrom(from);
292
0
}
293
294
0
bool MemcacheResponse::IsInitialized() const {
295
0
    return !_buf.empty();
296
0
}
297
298
0
void MemcacheResponse::Swap(MemcacheResponse* other) {
299
0
    if (other != this) {
300
0
        _buf.swap(other->_buf);
301
0
        std::swap(_cached_size_, other->_cached_size_);
302
0
    }
303
0
}
304
305
0
::google::protobuf::Metadata MemcacheResponse::GetMetadata() const {
306
0
    ::google::protobuf::Metadata metadata;
307
0
    metadata.descriptor = MemcacheResponse::descriptor();
308
0
    metadata.reflection = NULL;
309
0
    return metadata;
310
0
}
311
312
// ===================================================================
313
314
0
const char* MemcacheResponse::status_str(Status st) {
315
0
    switch (st) {
316
0
    case STATUS_SUCCESS:
317
0
        return "SUCCESS";
318
0
    case STATUS_KEY_ENOENT:
319
0
        return "The key does not exist";
320
0
    case STATUS_KEY_EEXISTS:
321
0
        return "The key exists";
322
0
    case STATUS_E2BIG:
323
0
        return "Arg list is too long";
324
0
    case STATUS_EINVAL:
325
0
        return "Invalid argument";
326
0
    case STATUS_NOT_STORED:
327
0
        return "Not stored";
328
0
    case STATUS_DELTA_BADVAL:
329
0
        return "Bad delta";
330
0
    case STATUS_AUTH_ERROR:
331
0
        return "authentication error";
332
0
    case STATUS_AUTH_CONTINUE:
333
0
        return "authentication continue";
334
0
    case STATUS_UNKNOWN_COMMAND:
335
0
        return "Unknown command";
336
0
    case STATUS_ENOMEM:
337
0
        return "Out of memory";
338
0
    }
339
0
    return "Unknown status";
340
0
}
341
342
// MUST NOT have extras.
343
// MUST have key.
344
// MUST NOT have value.
345
0
bool MemcacheRequest::GetOrDelete(uint8_t command, const butil::StringPiece& key) {
346
0
    const policy::MemcacheRequestHeader header = {
347
0
        policy::MC_MAGIC_REQUEST,
348
0
        command,
349
0
        butil::HostToNet16(key.size()),
350
0
        0,
351
0
        policy::MC_BINARY_RAW_BYTES,
352
0
        0,
353
0
        butil::HostToNet32(key.size()),
354
0
        0,
355
0
        0
356
0
    };
357
0
    if (_buf.append(&header, sizeof(header))) {
358
0
        return false;
359
0
    }
360
0
    if (_buf.append(key.data(), key.size())) {
361
0
        return false;
362
0
    }
363
0
    ++_pipelined_count;
364
0
    return true;
365
0
}
366
367
0
bool MemcacheRequest::Get(const butil::StringPiece& key) {
368
0
    return GetOrDelete(policy::MC_BINARY_GET, key);
369
0
}
370
371
0
bool MemcacheRequest::Delete(const butil::StringPiece& key) {
372
0
    return GetOrDelete(policy::MC_BINARY_DELETE, key);
373
0
}
374
375
struct FlushHeaderWithExtras {
376
    policy::MemcacheRequestHeader header;
377
    uint32_t exptime;
378
} __attribute__((packed));
379
BAIDU_CASSERT(sizeof(FlushHeaderWithExtras) == 28, must_match);
380
381
// MAY have extras.
382
// MUST NOT have key.
383
// MUST NOT have value.
384
// Extra data for flush:
385
//    Byte/     0       |       1       |       2       |       3       |
386
//       /              |               |               |               |
387
//      |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
388
//      +---------------+---------------+---------------+---------------+
389
//     0| Expiration                                                    |
390
//      +---------------+---------------+---------------+---------------+
391
//    Total 4 bytes
392
0
bool MemcacheRequest::Flush(uint32_t timeout) {
393
0
    const uint8_t FLUSH_EXTRAS = (timeout == 0 ? 0 : 4);
394
0
    FlushHeaderWithExtras header_with_extras = {{
395
0
            policy::MC_MAGIC_REQUEST,
396
0
            policy::MC_BINARY_FLUSH,
397
0
            0,
398
0
            FLUSH_EXTRAS,
399
0
            policy::MC_BINARY_RAW_BYTES,
400
0
            0,
401
0
            butil::HostToNet32(FLUSH_EXTRAS),
402
0
            0,
403
0
            0 }, butil::HostToNet32(timeout) };
404
0
    if (FLUSH_EXTRAS == 0) {
405
0
        if (_buf.append(&header_with_extras.header,
406
0
                       sizeof(policy::MemcacheRequestHeader))) {
407
0
            return false;
408
0
        }
409
0
    } else {
410
0
        if (_buf.append(&header_with_extras, sizeof(header_with_extras))) {
411
0
            return false;
412
0
        }
413
0
    }
414
0
    ++_pipelined_count;
415
0
    return true;
416
0
}
417
418
// (if found):
419
// MUST have extras.
420
// MAY have key.
421
// MAY have value.
422
// Extra data for the get commands:
423
// Byte/     0       |       1       |       2       |       3       |
424
//    /              |               |               |               |
425
//   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
426
//   +---------------+---------------+---------------+---------------+
427
//  0| Flags                                                         |
428
//   +---------------+---------------+---------------+---------------+
429
//   Total 4 bytes
430
bool MemcacheResponse::PopGet(
431
0
    butil::IOBuf* value, uint32_t* flags, uint64_t* cas_value) {
432
0
    const size_t n = _buf.size();
433
0
    policy::MemcacheResponseHeader header;
434
0
    if (n < sizeof(header)) {
435
0
        butil::string_printf(&_err, "buffer is too small to contain a header");
436
0
        return false;
437
0
    }
438
0
    _buf.copy_to(&header, sizeof(header));
439
0
    if (header.command != (uint8_t)policy::MC_BINARY_GET) {
440
0
        butil::string_printf(&_err, "not a GET response");
441
0
        return false;
442
0
    }
443
0
    if (n < sizeof(header) + header.total_body_length) {
444
0
        butil::string_printf(&_err, "response=%u < header=%u + body=%u",
445
0
                  (unsigned)n, (unsigned)sizeof(header), header.total_body_length);
446
0
        return false;
447
0
    }
448
0
    if (header.status != (uint16_t)STATUS_SUCCESS) {
449
0
        LOG_IF(ERROR, header.extras_length != 0) << "GET response must not have flags";
450
0
        LOG_IF(ERROR, header.key_length != 0) << "GET response must not have key";
451
0
        const int value_size = (int)header.total_body_length - (int)header.extras_length
452
0
            - (int)header.key_length;
453
0
        if (value_size < 0) {
454
0
            butil::string_printf(&_err, "value_size=%d is non-negative", value_size);
455
0
            return false;
456
0
        }
457
0
        _buf.pop_front(sizeof(header) + header.extras_length +
458
0
                      header.key_length);
459
0
        _err.clear();
460
0
        _buf.cutn(&_err, value_size);
461
0
        return false;
462
0
    }
463
0
    if (header.extras_length != 4u) {
464
0
        butil::string_printf(&_err, "GET response must have flags as extras, actual length=%u",
465
0
                  header.extras_length);
466
0
        return false;
467
0
    }
468
0
    if (header.key_length != 0) {
469
0
        butil::string_printf(&_err, "GET response must not have key");
470
0
        return false;
471
0
    }
472
0
    const int value_size = (int)header.total_body_length - (int)header.extras_length
473
0
        - (int)header.key_length;
474
0
    if (value_size < 0) {
475
0
        butil::string_printf(&_err, "value_size=%d is non-negative", value_size);
476
0
        return false;
477
0
    }
478
0
    _buf.pop_front(sizeof(header));
479
0
    uint32_t raw_flags = 0;
480
0
    _buf.cutn(&raw_flags, sizeof(raw_flags));
481
0
    if (flags) {
482
0
        *flags = butil::NetToHost32(raw_flags);
483
0
    }
484
0
    if (value) {
485
0
        value->clear();
486
0
        _buf.cutn(value, value_size);
487
0
    }
488
0
    if (cas_value) {
489
0
        *cas_value = header.cas_value;
490
0
    }
491
0
    _err.clear();
492
0
    return true;
493
0
}
494
495
bool MemcacheResponse::PopGet(
496
0
    std::string* value, uint32_t* flags, uint64_t* cas_value) {
497
0
    butil::IOBuf tmp;
498
0
    if (PopGet(&tmp, flags, cas_value)) {
499
0
        tmp.copy_to(value);
500
0
        return true;
501
0
    }
502
0
    return false;
503
0
}
504
505
// MUST NOT have extras
506
// MUST NOT have key
507
// MUST NOT have value
508
0
bool MemcacheResponse::PopDelete() {
509
0
    return PopStore(policy::MC_BINARY_DELETE, NULL);
510
0
}
511
0
bool MemcacheResponse::PopFlush() {
512
0
    return PopStore(policy::MC_BINARY_FLUSH, NULL);
513
0
}
514
515
struct StoreHeaderWithExtras {
516
    policy::MemcacheRequestHeader header;
517
    uint32_t flags;
518
    uint32_t exptime;
519
} __attribute__((packed));
520
BAIDU_CASSERT(sizeof(StoreHeaderWithExtras) == 32, must_match);
521
const size_t STORE_EXTRAS = sizeof(StoreHeaderWithExtras) -
522
                                                    sizeof(policy::MemcacheRequestHeader);
523
// MUST have extras.
524
// MUST have key.
525
// MAY have value.
526
// Extra data for set/add/replace:
527
// Byte/     0       |       1       |       2       |       3       |
528
//    /              |               |               |               |
529
//   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
530
//   +---------------+---------------+---------------+---------------+
531
//  0| Flags                                                         |
532
//   +---------------+---------------+---------------+---------------+
533
//  4| Expiration                                                    |
534
//   +---------------+---------------+---------------+---------------+
535
//   Total 8 bytes
536
bool MemcacheRequest::Store(
537
    uint8_t command, const butil::StringPiece& key, const butil::StringPiece& value,
538
0
    uint32_t flags, uint32_t exptime, uint64_t cas_value) {
539
0
    StoreHeaderWithExtras header_with_extras = {{
540
0
            policy::MC_MAGIC_REQUEST,
541
0
            command,
542
0
            butil::HostToNet16(key.size()),
543
0
            STORE_EXTRAS,
544
0
            policy::MC_BINARY_RAW_BYTES,
545
0
            0,
546
0
            butil::HostToNet32(STORE_EXTRAS + key.size() + value.size()),
547
0
            0,
548
0
            butil::HostToNet64(cas_value)
549
0
        }, butil::HostToNet32(flags), butil::HostToNet32(exptime)};
550
0
    if (_buf.append(&header_with_extras, sizeof(header_with_extras))) {
551
0
        return false;
552
0
    }
553
0
    if (_buf.append(key.data(), key.size())) {
554
0
        return false;
555
0
    }
556
0
    if (_buf.append(value.data(), value.size())) {
557
0
        return false;
558
0
    }
559
0
    ++_pipelined_count;
560
0
    return true;
561
0
}
562
563
// MUST have CAS
564
// MUST NOT have extras
565
// MUST NOT have key
566
// MUST NOT have value
567
0
bool MemcacheResponse::PopStore(uint8_t command, uint64_t* cas_value) {
568
0
    const size_t n = _buf.size();
569
0
    policy::MemcacheResponseHeader header;
570
0
    if (n < sizeof(header)) {
571
0
        butil::string_printf(&_err, "buffer is too small to contain a header");
572
0
        return false;
573
0
    }
574
0
    _buf.copy_to(&header, sizeof(header));
575
0
    if (header.command != command) {
576
0
        butil::string_printf(&_err, "Not a STORE response");
577
0
        return false;
578
0
    }
579
0
    if (n < sizeof(header) + header.total_body_length) {
580
0
        butil::string_printf(&_err, "Not enough data");
581
0
        return false;
582
0
    }
583
0
    LOG_IF(ERROR, header.extras_length != 0) << "STORE response must not have flags";
584
0
    LOG_IF(ERROR, header.key_length != 0) << "STORE response must not have key";
585
0
    int value_size = (int)header.total_body_length - (int)header.extras_length
586
0
        - (int)header.key_length;
587
0
    if (header.status != (uint16_t)STATUS_SUCCESS) {
588
0
        _buf.pop_front(sizeof(header) + header.extras_length + header.key_length);
589
0
        _err.clear();
590
0
        _buf.cutn(&_err, value_size);
591
0
        return false;
592
0
    }
593
0
    LOG_IF(ERROR, value_size != 0) << "STORE response must not have value, actually="
594
0
                                   << value_size;
595
0
    _buf.pop_front(sizeof(header) + header.total_body_length);
596
0
    if (cas_value) {
597
0
        CHECK(header.cas_value);
598
0
        *cas_value = header.cas_value;
599
0
    }
600
0
    _err.clear();
601
0
    return true;
602
0
}
603
604
bool MemcacheRequest::Set(
605
    const butil::StringPiece& key, const butil::StringPiece& value,
606
0
    uint32_t flags, uint32_t exptime, uint64_t cas_value) {
607
0
    return Store(policy::MC_BINARY_SET, key, value, flags, exptime, cas_value);
608
0
}
609
610
bool MemcacheRequest::Add(
611
    const butil::StringPiece& key, const butil::StringPiece& value,
612
0
    uint32_t flags, uint32_t exptime, uint64_t cas_value) {
613
0
    return Store(policy::MC_BINARY_ADD, key, value, flags, exptime, cas_value);
614
0
}
615
616
bool MemcacheRequest::Replace(
617
    const butil::StringPiece& key, const butil::StringPiece& value,
618
0
    uint32_t flags, uint32_t exptime, uint64_t cas_value) {
619
0
    return Store(policy::MC_BINARY_REPLACE, key, value, flags, exptime, cas_value);
620
0
}
621
    
622
bool MemcacheRequest::Append(
623
    const butil::StringPiece& key, const butil::StringPiece& value,
624
0
    uint32_t flags, uint32_t exptime, uint64_t cas_value) {
625
0
    if (value.empty()) {
626
0
        LOG(ERROR) << "value to append must be non-empty";
627
0
        return false;
628
0
    }
629
0
    return Store(policy::MC_BINARY_APPEND, key, value, flags, exptime, cas_value);
630
0
}
631
632
bool MemcacheRequest::Prepend(
633
    const butil::StringPiece& key, const butil::StringPiece& value,
634
0
    uint32_t flags, uint32_t exptime, uint64_t cas_value) {
635
0
    if (value.empty()) {
636
0
        LOG(ERROR) << "value to prepend must be non-empty";
637
0
        return false;
638
0
    }
639
0
    return Store(policy::MC_BINARY_PREPEND, key, value, flags, exptime, cas_value);
640
0
}
641
642
0
bool MemcacheResponse::PopSet(uint64_t* cas_value) {
643
0
    return PopStore(policy::MC_BINARY_SET, cas_value);
644
0
}
645
0
bool MemcacheResponse::PopAdd(uint64_t* cas_value) {
646
0
    return PopStore(policy::MC_BINARY_ADD, cas_value);
647
0
}
648
0
bool MemcacheResponse::PopReplace(uint64_t* cas_value) {
649
0
    return PopStore(policy::MC_BINARY_REPLACE, cas_value);
650
0
}
651
0
bool MemcacheResponse::PopAppend(uint64_t* cas_value) {
652
0
    return PopStore(policy::MC_BINARY_APPEND, cas_value);
653
0
}
654
0
bool MemcacheResponse::PopPrepend(uint64_t* cas_value) {
655
0
    return PopStore(policy::MC_BINARY_PREPEND, cas_value);
656
0
}
657
658
struct IncrHeaderWithExtras {
659
    policy::MemcacheRequestHeader header;
660
    uint64_t delta;
661
    uint64_t initial_value;
662
    uint32_t exptime;
663
} __attribute__((packed));
664
BAIDU_CASSERT(sizeof(IncrHeaderWithExtras) == 44, must_match);
665
666
const size_t INCR_EXTRAS = sizeof(IncrHeaderWithExtras) -
667
    sizeof(policy::MemcacheRequestHeader);
668
669
// MUST have extras.
670
// MUST have key.
671
// MUST NOT have value.
672
// Extra data for incr/decr:
673
// Byte/     0       |       1       |       2       |       3       |
674
//    /              |               |               |               |
675
//   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
676
//   +---------------+---------------+---------------+---------------+
677
//  0| Delta to add / subtract                                      |
678
//   |                                                               |
679
//   +---------------+---------------+---------------+---------------+
680
//  8| Initial value                                                 |
681
//   |                                                               |
682
//   +---------------+---------------+---------------+---------------+
683
// 16| Expiration                                                    |
684
//   +---------------+---------------+---------------+---------------+
685
//   Total 20 bytes
686
bool MemcacheRequest::Counter(
687
    uint8_t command, const butil::StringPiece& key, uint64_t delta,
688
0
    uint64_t initial_value, uint32_t exptime) {
689
0
    IncrHeaderWithExtras header_with_extras = {{
690
0
            policy::MC_MAGIC_REQUEST,
691
0
            command,
692
0
            butil::HostToNet16(key.size()),
693
0
            INCR_EXTRAS,
694
0
            policy::MC_BINARY_RAW_BYTES,
695
0
            0,
696
0
            butil::HostToNet32(INCR_EXTRAS + key.size()),
697
0
            0,
698
0
            0 }, butil::HostToNet64(delta), butil::HostToNet64(initial_value), butil::HostToNet32(exptime) };
699
0
    if (_buf.append(&header_with_extras, sizeof(header_with_extras))) {
700
0
        return false;
701
0
    }
702
0
    if (_buf.append(key.data(), key.size())) {
703
0
        return false;
704
0
    }
705
0
    ++_pipelined_count;
706
0
    return true;
707
0
}
708
709
bool MemcacheRequest::Increment(const butil::StringPiece& key, uint64_t delta,
710
0
                                uint64_t initial_value, uint32_t exptime) {
711
0
    return Counter(policy::MC_BINARY_INCREMENT, key, delta, initial_value, exptime);
712
0
}
713
714
bool MemcacheRequest::Decrement(const butil::StringPiece& key, uint64_t delta,
715
0
                                uint64_t initial_value, uint32_t exptime) {
716
0
    return Counter(policy::MC_BINARY_DECREMENT, key, delta, initial_value, exptime);
717
0
}
718
719
// MUST NOT have extras.
720
// MUST NOT have key.
721
// MUST have value.
722
// Byte/     0       |       1       |       2       |       3       |
723
//    /              |               |               |               |
724
//   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
725
//   +---------------+---------------+---------------+---------------+
726
//  0| 64-bit unsigned response.                                     |
727
//   |                                                               |
728
//   +---------------+---------------+---------------+---------------+
729
//   Total 8 bytes
730
bool MemcacheResponse::PopCounter(
731
0
    uint8_t command, uint64_t* new_value, uint64_t* cas_value) {
732
0
    const size_t n = _buf.size();
733
0
    policy::MemcacheResponseHeader header;
734
0
    if (n < sizeof(header)) {
735
0
        butil::string_printf(&_err, "buffer is too small to contain a header");
736
0
        return false;
737
0
    }
738
0
    _buf.copy_to(&header, sizeof(header));
739
0
    if (header.command != command) {
740
0
        butil::string_printf(&_err, "not a INCR/DECR response");
741
0
        return false;
742
0
    }
743
0
    if (n < sizeof(header) + header.total_body_length) {
744
0
        butil::string_printf(&_err, "response=%u < header=%u + body=%u",
745
0
                  (unsigned)n, (unsigned)sizeof(header), header.total_body_length);
746
0
        return false;
747
0
    }
748
0
    LOG_IF(ERROR, header.extras_length != 0) << "INCR/DECR response must not have flags";
749
0
    LOG_IF(ERROR, header.key_length != 0) << "INCR/DECR response must not have key";
750
0
    const int value_size = (int)header.total_body_length - (int)header.extras_length
751
0
        - (int)header.key_length;
752
0
    _buf.pop_front(sizeof(header) + header.extras_length + header.key_length);
753
754
0
    if (header.status != (uint16_t)STATUS_SUCCESS) {
755
0
        if (value_size < 0) {
756
0
            butil::string_printf(&_err, "value_size=%d is negative", value_size);
757
0
        } else {
758
0
            _err.clear();
759
0
            _buf.cutn(&_err, value_size);
760
0
        }
761
0
        return false;
762
0
    }
763
0
    if (value_size != 8) {
764
0
        butil::string_printf(&_err, "value_size=%d is not 8", value_size);
765
0
        return false;
766
0
    }
767
0
    uint64_t raw_value = 0;
768
0
    _buf.cutn(&raw_value, sizeof(raw_value));
769
0
    *new_value = butil::NetToHost64(raw_value);
770
0
    if (cas_value) {
771
0
        *cas_value = header.cas_value;
772
0
    }
773
0
    _err.clear();
774
0
    return true;
775
0
}
776
777
0
bool MemcacheResponse::PopIncrement(uint64_t* new_value, uint64_t* cas_value) {
778
0
    return PopCounter(policy::MC_BINARY_INCREMENT, new_value, cas_value);
779
0
}
780
0
bool MemcacheResponse::PopDecrement(uint64_t* new_value, uint64_t* cas_value) {
781
0
    return PopCounter(policy::MC_BINARY_DECREMENT, new_value, cas_value);
782
0
}
783
784
// MUST have extras.
785
// MUST have key.
786
// MUST NOT have value.
787
struct TouchHeaderWithExtras {
788
    policy::MemcacheRequestHeader header;
789
    uint32_t exptime;
790
} __attribute__((packed));
791
BAIDU_CASSERT(sizeof(TouchHeaderWithExtras) == 28, must_match);
792
const size_t TOUCH_EXTRAS = sizeof(TouchHeaderWithExtras) - sizeof(policy::MemcacheRequestHeader);
793
794
// MAY have extras.
795
// MUST NOT have key.
796
// MUST NOT have value.
797
// Extra data for touch:
798
//    Byte/     0       |       1       |       2       |       3       |
799
//       /              |               |               |               |
800
//      |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
801
//      +---------------+---------------+---------------+---------------+
802
//     0| Expiration                                                    |
803
//      +---------------+---------------+---------------+---------------+
804
//    Total 4 bytes
805
0
bool MemcacheRequest::Touch(const butil::StringPiece& key, uint32_t exptime) {
806
0
    TouchHeaderWithExtras header_with_extras = {{
807
0
            policy::MC_MAGIC_REQUEST,
808
0
            policy::MC_BINARY_TOUCH,
809
0
            butil::HostToNet16(key.size()),
810
0
            TOUCH_EXTRAS,
811
0
            policy::MC_BINARY_RAW_BYTES,
812
0
            0,
813
0
            butil::HostToNet32(TOUCH_EXTRAS + key.size()),
814
0
            0,
815
0
            0 }, butil::HostToNet32(exptime) };
816
0
    if (_buf.append(&header_with_extras, sizeof(header_with_extras))) {
817
0
        return false;
818
0
    }
819
0
    if (_buf.append(key.data(), key.size())) {
820
0
        return false;
821
0
    }
822
0
    ++_pipelined_count;
823
0
    return true;
824
0
}
825
826
// MUST NOT have extras.
827
// MUST NOT have key.
828
// MUST NOT have value.
829
0
bool MemcacheRequest::Version() {
830
0
    const policy::MemcacheRequestHeader header = {
831
0
        policy::MC_MAGIC_REQUEST,
832
0
        policy::MC_BINARY_VERSION,
833
0
        0,
834
0
        0,
835
0
        policy::MC_BINARY_RAW_BYTES,
836
0
        0,
837
0
        0,
838
0
        0,
839
0
        0
840
0
    };
841
0
    if (_buf.append(&header, sizeof(header))) {
842
0
        return false;
843
0
    }
844
0
    ++_pipelined_count;
845
0
    return true;
846
0
}
847
848
// MUST NOT have extras.
849
// MUST NOT have key.
850
// MUST have value.
851
0
bool MemcacheResponse::PopVersion(std::string* version) {
852
0
    const size_t n = _buf.size();
853
0
    policy::MemcacheResponseHeader header;
854
0
    if (n < sizeof(header)) {
855
0
        butil::string_printf(&_err, "buffer is too small to contain a header");
856
0
        return false;
857
0
    }
858
0
    _buf.copy_to(&header, sizeof(header));
859
0
    if (header.command != policy::MC_BINARY_VERSION) {
860
0
        butil::string_printf(&_err, "not a VERSION response");
861
0
        return false;
862
0
    }
863
0
    if (n < sizeof(header) + header.total_body_length) {
864
0
        butil::string_printf(&_err, "response=%u < header=%u + body=%u",
865
0
                  (unsigned)n, (unsigned)sizeof(header), header.total_body_length);
866
0
        return false;
867
0
    }
868
0
    LOG_IF(ERROR, header.extras_length != 0) << "VERSION response must not have flags";
869
0
    LOG_IF(ERROR, header.key_length != 0) << "VERSION response must not have key";
870
0
    const int value_size = (int)header.total_body_length - (int)header.extras_length
871
0
        - (int)header.key_length;
872
0
    _buf.pop_front(sizeof(header) + header.extras_length + header.key_length);
873
0
    if (value_size < 0) {
874
0
        butil::string_printf(&_err, "value_size=%d is negative", value_size);
875
0
        return false;
876
0
    }
877
0
    if (header.status != (uint16_t)STATUS_SUCCESS) {
878
0
        _err.clear();
879
0
        _buf.cutn(&_err, value_size);
880
0
        return false;
881
0
    }
882
0
    if (version) {
883
0
        version->clear();
884
0
        _buf.cutn(version, value_size);
885
0
    }
886
0
    _err.clear();
887
0
    return true;
888
0
}
889
 
890
} // namespace brpc