summaryrefslogtreecommitdiffstats
path: root/xpcom/io/nsSegmentedBuffer.h
blob: 3a064cedd557b9dea13d4120fd287616dceb4aad (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
/* -*- 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 nsSegmentedBuffer_h__
#define nsSegmentedBuffer_h__

#include <stddef.h>
#include <functional>

#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsTArray.h"
#include "mozilla/DataMutex.h"

class nsIEventTarget;

class nsSegmentedBuffer {
 public:
  nsSegmentedBuffer()
      : mSegmentSize(0),
        mMaxSize(0),
        mSegmentArray(nullptr),
        mSegmentArrayCount(0),
        mFirstSegmentIndex(0),
        mLastSegmentIndex(0) {}

  ~nsSegmentedBuffer() { Empty(); }

  nsresult Init(uint32_t aSegmentSize, uint32_t aMaxSize);

  char* AppendNewSegment();  // pushes at end

  // returns true if no more segments remain:
  bool DeleteFirstSegment();  // pops from beginning

  // returns true if no more segments remain:
  bool DeleteLastSegment();  // pops from beginning

  // Call Realloc() on last segment.  This is used to reduce memory
  // consumption when data is not an exact multiple of segment size.
  bool ReallocLastSegment(size_t aNewSize);

  void Empty();  // frees all segments

  inline uint32_t GetSegmentCount() {
    if (mFirstSegmentIndex <= mLastSegmentIndex) {
      return mLastSegmentIndex - mFirstSegmentIndex;
    } else {
      return mSegmentArrayCount + mLastSegmentIndex - mFirstSegmentIndex;
    }
  }

  inline uint32_t GetSegmentSize() { return mSegmentSize; }
  inline uint32_t GetMaxSize() { return mMaxSize; }
  inline uint32_t GetSize() { return GetSegmentCount() * mSegmentSize; }

  inline char* GetSegment(uint32_t aIndex) {
    NS_ASSERTION(aIndex < GetSegmentCount(), "index out of bounds");
    int32_t i = ModSegArraySize(mFirstSegmentIndex + (int32_t)aIndex);
    return mSegmentArray[i];
  }

 protected:
  inline int32_t ModSegArraySize(int32_t aIndex) {
    uint32_t result = aIndex & (mSegmentArrayCount - 1);
    NS_ASSERTION(result == aIndex % mSegmentArrayCount,
                 "non-power-of-2 mSegmentArrayCount");
    return result;
  }

  inline bool IsFull() {
    return ModSegArraySize(mLastSegmentIndex + 1) == mFirstSegmentIndex;
  }

 protected:
  uint32_t mSegmentSize;
  uint32_t mMaxSize;
  char** mSegmentArray;
  uint32_t mSegmentArrayCount;
  int32_t mFirstSegmentIndex;
  int32_t mLastSegmentIndex;

 private:
  class FreeOMTPointers {
    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FreeOMTPointers)

   public:
    FreeOMTPointers() : mTasks("nsSegmentedBuffer::FreeOMTPointers") {}

    void FreeAll();

    // Adds a task to the array. Returns the size of the array.
    size_t AddTask(std::function<void()>&& aTask) {
      auto tasks = mTasks.Lock();
      tasks->AppendElement(std::move(aTask));
      return tasks->Length();
    }

   private:
    ~FreeOMTPointers() = default;

    mozilla::DataMutex<nsTArray<std::function<void()>>> mTasks;
  };

  void FreeOMT(void* aPtr);
  void FreeOMT(std::function<void()>&& aTask);

  nsCOMPtr<nsIEventTarget> mIOThread;

  // This object is created the first time we need to dispatch to another thread
  // to free segments. It is only freed when the nsSegmentedBufer is destroyed
  // or when the runnable is finally handled and its refcount goes to 0.
  RefPtr<FreeOMTPointers> mFreeOMT;
};

// NS_SEGMENTARRAY_INITIAL_SIZE: This number needs to start out as a
// power of 2 given how it gets used. We double the segment array
// when we overflow it, and use that fact that it's a power of 2
// to compute a fast modulus operation in IsFull.
//
// 32 segment array entries can accommodate 128k of data if segments
// are 4k in size. That seems like a reasonable amount that will avoid
// needing to grow the segment array.
#define NS_SEGMENTARRAY_INITIAL_COUNT 32

#endif  // nsSegmentedBuffer_h__