summaryrefslogtreecommitdiffstats
path: root/ipc/glue/BigBuffer.h
blob: 7136b6c03c0dc28ca35ea2aa98521e5fa7c6ed45 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_ipc_BigBuffer_h
#define mozilla_ipc_BigBuffer_h

#include <stdlib.h>
#include <inttypes.h>
#include "mozilla/Span.h"
#include "mozilla/Variant.h"
#include "mozilla/ipc/SharedMemory.h"

namespace mozilla::ipc {

class BigBuffer {
 public:
  static constexpr size_t kShmemThreshold = 64 * 1024;

  static BigBuffer TryAlloc(const size_t aSize) {
    auto ret = BigBuffer{};
    auto data = TryAllocBuffer(aSize);
    if (data) {
      ret.mSize = aSize;
      ret.mData = std::move(data.ref());
    }
    return ret;
  }

  // Return a new BigBuffer which wraps no data.
  BigBuffer() : mSize(0), mData(NoData()) {}

  BigBuffer(const BigBuffer&) = delete;
  BigBuffer& operator=(const BigBuffer&) = delete;

  BigBuffer(BigBuffer&& aOther) noexcept
      : mSize(std::exchange(aOther.mSize, 0)),
        mData(std::exchange(aOther.mData, NoData())) {}

  BigBuffer& operator=(BigBuffer&& aOther) noexcept {
    mSize = std::exchange(aOther.mSize, 0);
    mData = std::exchange(aOther.mData, NoData());
    return *this;
  }

  // Create a new BigBuffer with the given size.
  // The buffer will be created uninitialized and must be fully initialized
  // before sending over IPC to avoid leaking uninitialized memory to another
  // process.
  explicit BigBuffer(size_t aSize) : mSize(aSize), mData(AllocBuffer(aSize)) {}

  // Create a new BigBuffer containing the data from the provided byte slice.
  explicit BigBuffer(Span<const uint8_t> aData) : BigBuffer(aData.Length()) {
    memcpy(Data(), aData.Elements(), aData.Length());
  }

  // Marker to indicate that a particular constructor of BigBuffer adopts
  // ownership of the provided data.
  struct Adopt {};

  // Create a new BigBuffer from an existing shared memory region, taking
  // ownership of that shared memory region. The shared memory region must be
  // non-null, mapped, and large enough to fit aSize bytes.
  BigBuffer(Adopt, SharedMemory* aSharedMemory, size_t aSize);

  // Create a new BigBuffer from an existing memory buffer, taking ownership of
  // that memory region. The region will be freed using `free()` when it is no
  // longer needed.
  BigBuffer(Adopt, uint8_t* aData, size_t aSize);

  ~BigBuffer() = default;

  // Returns a pointer to the data stored by this BigBuffer, regardless of
  // backing storage type.
  uint8_t* Data();
  const uint8_t* Data() const;

  // Returns the size of the data stored by this BigBuffer, regardless of
  // backing storage type.
  size_t Size() const { return mSize; }

  // Get a view of the BigBuffer's data as a span.
  Span<uint8_t> AsSpan() { return Span{Data(), Size()}; }
  Span<const uint8_t> AsSpan() const { return Span{Data(), Size()}; }

  // If the BigBuffer is backed by shared memory, returns a pointer to the
  // backing SharedMemory region.
  SharedMemory* GetSharedMemory() const {
    return mData.is<1>() ? mData.as<1>().get() : nullptr;
  }

 private:
  friend struct IPC::ParamTraits<mozilla::ipc::BigBuffer>;

  using Storage = Variant<UniqueFreePtr<uint8_t[]>, RefPtr<SharedMemory>>;

  // Empty storage which holds no data.
  static Storage NoData() { return AsVariant(UniqueFreePtr<uint8_t[]>{}); }

  // Fallibly allocate a new storage of the given size.
  static Maybe<Storage> TryAllocBuffer(size_t aSize);

  // Infallibly allocate a new storage of the given size.
  static Storage AllocBuffer(size_t aSize) {
    auto ret = TryAllocBuffer(aSize);
    if (!ret) {
      NS_ABORT_OOM(aSize);
    }
    return std::move(ret.ref());
  }

  size_t mSize;
  Storage mData;
};

}  // namespace mozilla::ipc

namespace IPC {

template <>
struct ParamTraits<mozilla::ipc::BigBuffer> {
  using paramType = mozilla::ipc::BigBuffer;
  static void Write(MessageWriter* aWriter, paramType&& aParam);
  static bool Read(MessageReader* aReader, paramType* aResult);
};

}  // namespace IPC

#endif  // mozilla_BigBuffer_h