frame_format.cc
1 // Copyright 2018, Beeri 15. All rights reserved.
2 // Author: Roman Gershman (romange@gmail.com)
3 //
4 
5 #include "util/rpc/frame_format.h"
6 
7 #include <boost/asio/read.hpp>
8 
9 #include "absl/strings/escaping.h"
10 #include "base/bits.h"
11 #include "base/endian.h"
12 #include "base/logging.h"
13 #include "util/asio/yield.h"
14 #include "util/asio/error.h"
15 
16 namespace util {
17 using fibers_ext::yield;
18 
19 namespace rpc {
20 using namespace boost;
21 using namespace system;
22 
23 namespace {
24 
25 constexpr uint8 kHeader[] = "URPC";
26 
27 inline uint8 SizeByteCountMinus1(uint32 size) {
28  return size <= 255 ? 0 : Bits::FindMSBSetNonZero(size) / 8;
29 }
30 
31 inline constexpr uint32 byte_mask(uint8 n) {
32  return (1UL << (n + 1) * 8) - 1;
33 }
34 
35 } // namespace
36 
37 std::ostream& operator<<(std::ostream& o, const Frame& frame) {
38  o << "{ rpc_id " << frame.rpc_id << ", header_size: " << frame.header_size
39  << ", letter_size: " << frame.letter_size << " }";
40  return o;
41 }
42 
43 const uint32 Frame::kHeaderVal = LittleEndian::Load32(kHeader);
44 
45 uint8_t Frame::DecodeStart(const uint8_t* src, ::boost::system::error_code& ec) {
46  if (kHeaderVal != LittleEndian::Load32(src)) {
47  ec = make_error_code(gaia_error::bad_header);
48  return 0;
49  }
50 
51  if (src[4] >> 4 != 0) { // version check
52  ec = make_error_code(gaia_error::invalid_version);
53  return 0;
54  }
55  rpc_id = UNALIGNED_LOAD64(src + 4);
56  rpc_id >>= 8;
57 
58  return src[4] & 15;
59 }
60 
61 void Frame::DecodeEnd(const uint8_t* src, uint8_t hsz_len, uint8_t lsz_len) {
62  header_size = LittleEndian::Load32(src) & byte_mask(hsz_len);
63  letter_size = LittleEndian::Load32(src + hsz_len + 1) & byte_mask(lsz_len);
64 }
65 
66 unsigned Frame::Write(uint8* dest) const {
67  LittleEndian::Store32(dest, kHeaderVal);
68  dest += 4;
69  const uint8 msg_bytes_minus1 = SizeByteCountMinus1(letter_size);
70  const uint8 cntrl_bytes_minus1 = SizeByteCountMinus1(header_size);
71 
72  DCHECK_LT(msg_bytes_minus1, 4);
73  DCHECK_LT(cntrl_bytes_minus1, 4);
74 
75  uint64_t version = cntrl_bytes_minus1 | (msg_bytes_minus1 << 2) | (0 << 4);
76 
77  LittleEndian::Store64(dest, (rpc_id << 8) | version);
78  dest += 8;
79 
80  LittleEndian::Store32(dest, header_size);
81  dest += (cntrl_bytes_minus1 + 1);
82  LittleEndian::Store32(dest, letter_size);
83 
84  return 4 + 1 /* version */ + 7 /* rpc_id */ + cntrl_bytes_minus1 + msg_bytes_minus1 + 2;
85 }
86 
87 } // namespace rpc
88 
89 } // namespace util