message.c++ 10 KB
Newer Older
1
// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
Kenton Varda's avatar
Kenton Varda committed
2
// Licensed under the MIT License:
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
21

Kenton Varda's avatar
Kenton Varda committed
22
#define CAPNP_PRIVATE
Kenton Varda's avatar
Kenton Varda committed
23
#include "message.h"
Kenton Varda's avatar
Kenton Varda committed
24
#include <kj/debug.h>
25
#include "arena.h"
Kenton Varda's avatar
Kenton Varda committed
26
#include "orphan.h"
27
#include <stdlib.h>
28 29
#include <exception>
#include <string>
30
#include <vector>
31
#include <errno.h>
32

33
namespace capnp {
34

35 36 37 38
namespace {

class DummyCapTableReader: public _::CapTableReader {
public:
39
#if !CAPNP_LITE
40 41 42
  kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override {
    return nullptr;
  }
43
#endif
44 45 46 47 48
};
static constexpr DummyCapTableReader dummyCapTableReader = DummyCapTableReader();

}  // namespace

49
MessageReader::MessageReader(ReaderOptions options): options(options), allocatedArena(false) {}
50
MessageReader::~MessageReader() noexcept(false) {
51
  if (allocatedArena) {
52
    arena()->~ReaderArena();
53 54 55
  }
}

Matthew Maurer's avatar
Matthew Maurer committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
bool MessageReader::isCanonical() {
  if (!allocatedArena) {
    static_assert(sizeof(_::ReaderArena) <= sizeof(arenaSpace),
        "arenaSpace is too small to hold a ReaderArena.  Please increase it.  This will break "
        "ABI compatibility.");
    new(arena()) _::ReaderArena(this);
    allocatedArena = true;
  }

  _::SegmentReader *segment = arena()->tryGetSegment(_::SegmentId(0));

  if (segment == NULL) {
    // The message has no segments
    return false;
  }

  if (arena()->tryGetSegment(_::SegmentId(1))) {
    // The message has more than one segment
    return false;
  }

  const word* readHead = segment->getStartPtr() + 1;
  return _::PointerReader::getRoot(segment, nullptr, segment->getStartPtr(),
                                   this->getOptions().nestingLimit)
                                  .isCanonical(&readHead);
}


84
AnyPointer::Reader MessageReader::getRootInternal() {
85
  if (!allocatedArena) {
86 87
    static_assert(sizeof(_::ReaderArena) <= sizeof(arenaSpace),
        "arenaSpace is too small to hold a ReaderArena.  Please increase it.  This will break "
88
        "ABI compatibility.");
89
    new(arena()) _::ReaderArena(this);
90 91 92
    allocatedArena = true;
  }

93
  _::SegmentReader* segment = arena()->tryGetSegment(_::SegmentId(0));
94 95 96
  KJ_REQUIRE(segment != nullptr &&
             segment->containsInterval(segment->getStartPtr(), segment->getStartPtr() + 1),
             "Message did not contain a root pointer.") {
97
    return AnyPointer::Reader();
98
  }
99

100
  // const_cast here is safe because dummyCapTableReader has no state.
101
  return AnyPointer::Reader(_::PointerReader::getRoot(
102 103
      segment, const_cast<DummyCapTableReader*>(&dummyCapTableReader),
      segment->getStartPtr(), options.nestingLimit));
104 105 106 107 108
}

// -------------------------------------------------------------------

MessageBuilder::MessageBuilder(): allocatedArena(false) {}
109

110
MessageBuilder::~MessageBuilder() noexcept(false) {
111
  if (allocatedArena) {
112
    kj::dtor(*arena());
113 114 115
  }
}

116 117 118 119 120 121
MessageBuilder::MessageBuilder(kj::ArrayPtr<SegmentInit> segments)
    : allocatedArena(false) {
  kj::ctor(*arena(), this, segments);
  allocatedArena = true;
}

122
_::SegmentBuilder* MessageBuilder::getRootSegment() {
123
  if (allocatedArena) {
124
    return arena()->getSegment(_::SegmentId(0));
125
  } else {
126
    static_assert(sizeof(_::BuilderArena) <= sizeof(arenaSpace),
127 128
        "arenaSpace is too small to hold a BuilderArena.  Please increase it.");
    kj::ctor(*arena(), this);
129
    allocatedArena = true;
130

131 132 133
    auto allocation = arena()->allocate(POINTER_SIZE_IN_WORDS);

    KJ_ASSERT(allocation.segment->getSegmentId() == _::SegmentId(0),
134
        "First allocated word of new arena was not in segment ID 0.");
135
    KJ_ASSERT(allocation.words == allocation.segment->getPtrUnchecked(0 * WORDS),
136
        "First allocated word of new arena was not the first word in its segment.");
137
    return allocation.segment;
138
  }
139 140
}

141
AnyPointer::Builder MessageBuilder::getRootInternal() {
142
  _::SegmentBuilder* rootSegment = getRootSegment();
143
  return AnyPointer::Builder(_::PointerBuilder::getRoot(
144
      rootSegment, arena()->getLocalCapTable(), rootSegment->getPtrUnchecked(0 * WORDS)));
145 146
}

147
kj::ArrayPtr<const kj::ArrayPtr<const word>> MessageBuilder::getSegmentsForOutput() {
148 149 150 151 152 153 154
  if (allocatedArena) {
    return arena()->getSegmentsForOutput();
  } else {
    return nullptr;
  }
}

155 156 157 158 159
Orphanage MessageBuilder::getOrphanage() {
  // We must ensure that the arena and root pointer have been allocated before the Orphanage
  // can be used.
  if (!allocatedArena) getRootSegment();

160
  return Orphanage(arena(), arena()->getLocalCapTable());
161 162
}

Matthew Maurer's avatar
Matthew Maurer committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176
bool MessageBuilder::isCanonical() {
  _::SegmentReader *segment = getRootSegment();

  if (segment == NULL) {
    // The message has no segments
    return false;
  }

  if (arena()->tryGetSegment(_::SegmentId(1))) {
    // The message has more than one segment
    return false;
  }

  const word* readHead = segment->getStartPtr() + 1;
Matthew Maurer's avatar
Matthew Maurer committed
177
  return _::PointerReader::getRoot(segment, nullptr, segment->getStartPtr(), kj::maxValue)
Matthew Maurer's avatar
Matthew Maurer committed
178 179 180
                                  .isCanonical(&readHead);
}

181 182 183
// =======================================================================================

SegmentArrayMessageReader::SegmentArrayMessageReader(
184
    kj::ArrayPtr<const kj::ArrayPtr<const word>> segments, ReaderOptions options)
185
    : MessageReader(options), segments(segments) {}
186

187
SegmentArrayMessageReader::~SegmentArrayMessageReader() noexcept(false) {}
188

189
kj::ArrayPtr<const word> SegmentArrayMessageReader::getSegment(uint id) {
190 191
  if (id < segments.size()) {
    return segments[id];
Kenton Varda's avatar
Kenton Varda committed
192
  } else {
193
    return nullptr;
Kenton Varda's avatar
Kenton Varda committed
194 195 196
  }
}

197
// -------------------------------------------------------------------
198

199 200 201
struct MallocMessageBuilder::MoreSegments {
  std::vector<void*> segments;
};
202

203 204 205
MallocMessageBuilder::MallocMessageBuilder(
    uint firstSegmentWords, AllocationStrategy allocationStrategy)
    : nextSize(firstSegmentWords), allocationStrategy(allocationStrategy),
206
      ownFirstSegment(true), returnedFirstSegment(false), firstSegment(nullptr) {}
207 208

MallocMessageBuilder::MallocMessageBuilder(
209
    kj::ArrayPtr<word> firstSegment, AllocationStrategy allocationStrategy)
210
    : nextSize(firstSegment.size()), allocationStrategy(allocationStrategy),
211
      ownFirstSegment(false), returnedFirstSegment(false), firstSegment(firstSegment.begin()) {
212
  KJ_REQUIRE(firstSegment.size() > 0, "First segment size must be non-zero.");
213 214

  // Checking just the first word should catch most cases of failing to zero the segment.
215
  KJ_REQUIRE(*reinterpret_cast<uint64_t*>(firstSegment.begin()) == 0,
216
          "First segment must be zeroed.");
217
}
218

219
MallocMessageBuilder::~MallocMessageBuilder() noexcept(false) {
220 221 222 223 224
  if (returnedFirstSegment) {
    if (ownFirstSegment) {
      free(firstSegment);
    } else {
      // Must zero first segment.
225
      kj::ArrayPtr<const kj::ArrayPtr<const word>> segments = getSegmentsForOutput();
226
      if (segments.size() > 0) {
227
        KJ_ASSERT(segments[0].begin() == firstSegment,
228 229 230
            "First segment in getSegmentsForOutput() is not the first segment allocated?");
        memset(firstSegment, 0, segments[0].size() * sizeof(word));
      }
Kenton Varda's avatar
Kenton Varda committed
231
    }
232

233
    KJ_IF_MAYBE(s, moreSegments) {
234
      for (void* ptr: s->get()->segments) {
235 236
        free(ptr);
      }
237 238
    }
  }
239
}
Kenton Varda's avatar
Kenton Varda committed
240

241
kj::ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) {
242
  if (!returnedFirstSegment && !ownFirstSegment) {
243
    kj::ArrayPtr<word> result = kj::arrayPtr(reinterpret_cast<word*>(firstSegment), nextSize);
244
    if (result.size() >= minimumSize) {
245
      returnedFirstSegment = true;
246 247 248 249 250
      return result;
    }
    // If the provided first segment wasn't big enough, we discard it and proceed to allocate
    // our own.  This never happens in practice since minimumSize is always 1 for the first
    // segment.
251
    ownFirstSegment = true;
252 253
  }

254
  uint size = kj::max(minimumSize, nextSize);
Kenton Varda's avatar
Kenton Varda committed
255

256 257
  void* result = calloc(size, sizeof(word));
  if (result == nullptr) {
258
    KJ_FAIL_SYSCALL("calloc(size, sizeof(word))", ENOMEM, size);
259
  }
Kenton Varda's avatar
Kenton Varda committed
260

261
  if (!returnedFirstSegment) {
262
    firstSegment = result;
263 264 265
    returnedFirstSegment = true;

    // After the first segment, we want nextSize to equal the total size allocated so far.
266 267
    if (allocationStrategy == AllocationStrategy::GROW_HEURISTICALLY) nextSize = size;
  } else {
268 269
    MoreSegments* segments;
    KJ_IF_MAYBE(s, moreSegments) {
270
      segments = *s;
271 272 273 274
    } else {
      auto newSegments = kj::heap<MoreSegments>();
      segments = newSegments;
      moreSegments = mv(newSegments);
275
    }
276
    segments->segments.push_back(result);
277 278
    if (allocationStrategy == AllocationStrategy::GROW_HEURISTICALLY) nextSize += size;
  }
Kenton Varda's avatar
Kenton Varda committed
279

280
  return kj::arrayPtr(reinterpret_cast<word*>(result), size);
Kenton Varda's avatar
Kenton Varda committed
281 282
}

283 284
// -------------------------------------------------------------------

285
FlatMessageBuilder::FlatMessageBuilder(kj::ArrayPtr<word> array): array(array), allocated(false) {}
286
FlatMessageBuilder::~FlatMessageBuilder() noexcept(false) {}
287 288

void FlatMessageBuilder::requireFilled() {
289
  KJ_REQUIRE(getSegmentsForOutput()[0].end() == array.end(),
290 291 292
          "FlatMessageBuilder's buffer was too large.");
}

293
kj::ArrayPtr<word> FlatMessageBuilder::allocateSegment(uint minimumSize) {
294
  KJ_REQUIRE(!allocated, "FlatMessageBuilder's buffer was not large enough.");
295 296 297 298
  allocated = true;
  return array;
}

299
}  // namespace capnp