text:
namespace base {

namespace {

const uint32_t TEST_MEMORY_SIZE = 1 << 20;   // 1 MiB
const uint32_t TEST_MEMORY_PAGE = 64 << 10;  // 64 KiB
const uint32_t TEST_ID = 12345;
const char TEST_NAME[] = "TestAllocator";

void SetFileLength(const base::FilePath& path, size_t length) {
  {
    File file(path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
    DCHECK(file.IsValid());
    ASSERT_TRUE(file.SetLength(static_cast<int64_t>(length)));
  }

  int64_t actual_length;
  DCHECK(GetFileSize(path, &actual_length));
  DCHECK_EQ(length, static_cast<size_t>(actual_length));
}

}  // namespace

typedef PersistentMemoryAllocator::Reference Reference;

class PersistentMemoryAllocatorTest : public testing::Test {
 public:
  // This can't be statically initialized because it's value isn't defined
  // in the PersistentMemoryAllocator header file. Instead, it's simply set
  // in the constructor.
  uint32_t kAllocAlignment;

  struct TestObject1 {
    static constexpr uint32_t kPersistentTypeId = 1;
    static constexpr size_t kExpectedInstanceSize = 4 + 1 + 3;
    int32_t onething;
    char oranother;
  };

  struct TestObject2 {
    static constexpr uint32_t kPersistentTypeId = 2;
    static constexpr size_t kExpectedInstanceSize = 8 + 4 + 4 + 8 + 8;
    int64_t thiis;
    int32_t that;
    float andthe;
    double other;
    char thing[8];
  };

  PersistentMemoryAllocatorTest() {
    kAllocAlignment = GetAllocAlignment();
    mem_segment_.reset(new char[TEST_MEMORY_SIZE]);
  }

  void SetUp() override {
    allocator_.reset();
    ::memset(mem_segment_.get(), 0, TEST_MEMORY_SIZE);
    allocator_ = std::make_unique<PersistentMemoryAllocator>(
        mem_segment_.get(), TEST_MEMORY_SIZE, TEST_MEMORY_PAGE, TEST_ID,
        TEST_NAME, false);
  }

  void TearDown() override {
    allocator_.reset();
  }

  unsigned CountIterables() {
    PersistentMemoryAllocator::Iterator iter(allocator_.get());
    uint32_t type;
    unsigned count = 0;
    while (iter.GetNext(&type) != 0) {
      ++count;
    }
    return count;
  }

  static uint32_t GetAllocAlignment() {
    return PersistentMemoryAllocator::kAllocAlignment;
  }

 protected:
  std::unique_ptr<char[]> mem_segment_;
  std::unique_ptr<PersistentMemoryAllocator> allocator_;
};

TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) {
  allocator_->CreateTrackingHistograms(allocator_->Name());

  std::string base_name(TEST_NAME);
  EXPECT_EQ(TEST_ID, allocator_->Id());
  EXPECT_TRUE(allocator_->used_histogram_);
  EXPECT_EQ("UMA.PersistentAllocator." + base_name + ".UsedPct",
            allocator_->used_histogram_->histogram_name());
  EXPECT_EQ(PersistentMemoryAllocator::MEMORY_INITIALIZED,
            allocator_->GetMemoryState());

  // Get base memory info for later comparison.
  PersistentMemoryAllocator::MemoryInfo meminfo0;
  allocator_->GetMemoryInfo(&meminfo0);
  EXPECT_EQ(TEST_MEMORY_SIZE, meminfo0.total);
  EXPECT_GT(meminfo0.total, meminfo0.free);

  // Validate allocation of test object and make sure it can be referenced
  // and all metadata looks correct.
  TestObject1* obj1 = allocator_->New<TestObject1>();
  ASSERT_TRUE(obj1);
  Reference block1 = allocator_->GetAsReference(obj1);
  ASSERT_NE(0U, block1);
  EXPECT_NE(nullptr, allocator_->GetAsObject<TestObject1>(block1));
  EXPECT_EQ(nullptr, allocator_->GetAsObject<TestObject2>(block1));
  EXPECT_LE(sizeof(TestObject1), allocator_->GetAllocSize(block1));
  EXPECT_GT(sizeof(TestObject1) + kAllocAlignment,
            allocator_->GetAllocSize(block1));
  PersistentMemoryAllocator::MemoryInfo meminfo1;
  allocator_->GetMemoryInfo(&meminfo1);
  EXPECT_EQ(meminfo0.total, meminfo1.total);
  EXPECT_GT(meminfo0.free, meminfo1.free);

  // Verify that pointers can be turned back into references and that invalid
  // addresses return null.
  char* memory1 = allocator_->GetAsArray<char>(block1, 1, 1);
  ASSERT_TRUE(memory1);
  EXPECT_EQ(block1, allocator_->GetAsReference(memory1, 0));
  EXPECT_EQ(block1, allocator_->GetAsReference(memory1, 1));
  EXPECT_EQ(0U, allocator_->GetAsReference(memory1, 2));
  EXPECT_EQ(0U, allocator_->GetAsReference(memory1 + 1, 0));
  EXPECT_EQ(0U, allocator_->GetAsReference(memory1 + 16, 0));
  EXPECT_EQ(0U, allocator_->GetAsReference(nullptr, 0));
  EXPECT_EQ(0U, allocator_->GetAsReference(&base_name, 0));

  // Ensure that the test-object can be made iterable.
  PersistentMemoryAllocator::Iterator iter1a(allocator_.get());
  EXPECT_EQ(0U, iter1a.GetLast());
  uint32_t type;
  EXPECT_EQ(0U, iter1a.GetNext(&type));
  allocator_->MakeIterable(block1);
  EXPECT_EQ(block1, iter1a.GetNext(&type));
  EXPECT_EQ(1U, type);
  EXPECT_EQ(block1, iter1a.GetLast());
  EXPECT_EQ(0U, iter1a.GetNext(&type));
  EXPECT_EQ(block1, iter1a.GetLast());

  // Create second test-object and ensure everything is good and it cannot
  // be confused with test-object of another type.
  TestObject2* obj2 = allocator_->New<TestObject2>();
  ASSERT_TRUE(obj2);
  Reference block2 = allocator_->GetAsReference(obj2);
  ASSERT_NE(0U, block2);
  EXPECT_NE(nullptr, allocator_->GetAsObject<TestObject2>(block2));
  EXPECT_EQ(nullptr, allocator_->GetAsObject<TestObject1>(block2));
  EXPECT_LE(sizeof(TestObject2), allocator_->GetAllocSize(block2));
  EXPECT_GT(sizeof(TestObject2) + kAllocAlignment,
            allocator_->GetAllocSize(block2));
  PersistentMemoryAllocator::MemoryInfo meminfo2;
  allocator_->GetMemoryInfo(&meminfo2);
  EXPECT_EQ(meminfo1.total, meminfo2.total);
  EXPECT_GT(meminfo1.free, meminfo2.free);

  // Ensure that second test-object can also be made iterable.
  allocator_->MakeIterable(obj2);
  EXPECT_EQ(block2, iter1a.GetNext(&type));
  EXPECT_EQ(2U, type);
  EXPECT_EQ(block2, iter1a.GetLast());
  EXPECT_EQ(0U, iter1a.GetNext(&type));
  EXPECT_EQ(block2, iter1a.GetLast());

  // Check that the iterator can be reset to the beginning.
  iter1a.Reset();
  EXPECT_EQ(0U, iter1a.GetLast());
  EXPECT_EQ(block1, iter1a.GetNext(&type));
  EXPECT_EQ(block1, iter1a.GetLast());
  EXPECT_EQ(block2, iter1a.GetNext(&type));
  EXPECT_EQ(block2, iter1a.GetLast());
  EXPECT_EQ(0U, iter1a.GetNext(&type));

  // Check that the iterator can be reset to an arbitrary location.
  iter1a.Reset(block1);
  EXPECT_EQ(block1, iter1a.GetLast());
  EXPECT_EQ(block2, iter1a.GetNext(&type));
  EXPECT_EQ(block2, iter1a.GetLast());
  EXPECT_EQ(0U, iter1a.GetNext(&type));

  // Check that iteration can begin after an arbitrary location.
  PersistentMemoryAllocator::Iterator iter1b(allocator_.get(), block1);
  EXPECT_EQ(block2, iter1b.GetNext(&type));
  EXPECT_EQ(0U, iter1b.GetNext(&type));

  // Ensure nothing has gone noticably wrong.
  EXPECT_FALSE(allocator_->IsFull());
  EXPECT_FALSE(allocator_->IsCorrupt());

  // Check the internal histogram record of used memory.
  allocator_->UpdateTrackingHistograms();
  std::unique_ptr<HistogramSamples> used_samples(
      allocator_->used_histogram_->SnapshotSamples());
  EXPECT_TRUE(used_samples);
  EXPECT_EQ(1, used_samples->TotalCount());

  // Check that an object's type can be changed.
  EXPECT_EQ(2U, allocator_->GetType(block2));
  allocator_->ChangeType(block2, 3, 2, false);
  EXPECT_EQ(3U, allocator_->GetType(block2));
  allocator_->New<TestObject2>(block2, 3, false);
  EXPECT_EQ(2U, allocator_->GetType(block2));

  // Create second allocator (read/write) using the same memory segment.
  std::unique_ptr<PersistentMemoryAllocator> allocator2(
      new PersistentMemoryAllocator(mem_segment_.get(), TEST_MEMORY_SIZE,
                                    TEST_MEMORY_PAGE, 0, "", false));
  EXPECT_EQ(TEST_ID, allocator2->Id());
  EXPECT_FALSE(allocator2->used_histogram_);

  // Ensure that iteration and access through second allocator works.
  PersistentMemoryAllocator::Iterator iter2(allocator2.get());
  EXPECT_EQ(block1, iter2.GetNext(&type));
  EXPECT_EQ(block2, iter2.GetNext(&type));
  EXPECT_EQ(0U, iter2.GetNext(&type));
  EXPECT_NE(nullptr, allocator2->GetAsObject<TestObject1>(block1));
  EXPECT_NE(nullptr, allocator2->GetAsObject<TestObject2>(block2));

  // Create a third allocator (read-only) using the same memory segment.
  std::unique_ptr<const PersistentMemoryAllocator> allocator3(
      new PersistentMemoryAllocator(mem_segment_.get(), TEST_MEMORY_SIZE,
                                    TEST_MEMORY_PAGE, 0, "", true));
  EXPECT_EQ(TEST_ID, allocator3->Id());
  EXPECT_FALSE(allocator3->used_histogram_);

  // Ensure that iteration and access through third allocator works.
  PersistentMemoryAllocator::Iterator iter3(allocator3.get());
  EXPECT_EQ(block1, iter3.GetNext(&type));
  EXPECT_EQ(block2, iter3.GetNext(&type));
  EXPECT_EQ(0U, iter3.GetNext(&type));
  EXPECT_NE(nullptr, allocator3->GetAsObject<TestObject1>(block1));
  EXPECT_NE(nullptr, allocator3->GetAsObject<TestObject2>(block2));

  // Ensure that GetNextOfType works.
  PersistentMemoryAllocator::Iterator iter1c(allocator_.get());
  EXPECT_EQ(block2, iter1c.GetNextOfType<TestObject2>());
  EXPECT_EQ(0U, iter1c.GetNextOfType(2));

  // Ensure that GetNextOfObject works.
  PersistentMemoryAllocator::Iterator iter1d(allocator_.get());
  EXPECT_EQ(obj2, iter1d.GetNextOfObject<TestObject2>());
  EXPECT_EQ(nullptr, iter1d.GetNextOfObject<TestObject2>());

  // Ensure that deleting an object works.
  allocator_->Delete(obj2);
  PersistentMemoryAllocator::Iterator iter1z(allocator_.get());
  EXPECT_EQ(nullptr, iter1z.GetNextOfObject<TestObject2>());

  // Ensure that the memory state can be set.
  allocator_->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
  EXPECT_EQ(PersistentMemoryAllocator::MEMORY_DELETED,
            allocator_->GetMemoryState());
}

TEST_F(PersistentMemoryAllocatorTest, PageTest) {
  // This allocation will go into the first memory page.
  Reference block1 = allocator_->Allocate(TEST_MEMORY_PAGE / 2, 1);
  EXPECT_LT(0U, block1);
  EXPECT_GT(TEST_MEMORY_PAGE, block1);

  // This allocation won't fit in same page as previous block.
  Reference block2 =
      allocator_->Allocate(TEST_MEMORY_PAGE - 2 * kAllocAlignment, 2);
  EXPECT_EQ(TEST_MEMORY_PAGE, block2);

  // This allocation will also require a new page.
  Reference block3 = allocator_->Allocate(2 * kAllocAlignment + 99, 3);
  EXPECT_EQ(2U * TEST_MEMORY_PAGE, block3);
}

// A simple thread that takes an allocator and repeatedly allocates random-
// sized chunks from it until no more can be done.
class AllocatorThread : public SimpleThread {
 public:
  AllocatorThread(const std::string& name,
                  void* base,
                  uint32_t size,
                  uint32_t page_size)
      : SimpleThread(name, Options()),
        count_(0),
        iterable_(0),
        allocator_(base, size, page_size, 0, std::string(), false) {}

  void Run() override {
    for (;;) {
      uint32_t size = RandInt(1, 99);
      uint32_t type = RandInt(100, 999);
      Reference block = allocator_.Allocate(size, type);
      if (!block)
        break;

      count_++;
      if (RandInt(0, 1)) {
        allocator_.MakeIterable(block);
        iterable_++;
      }
    }
  }

  unsigned iterable() { return iterable_; }
  unsigned count() { return count_; }

 private:
  unsigned count_;
  unsigned iterable_;
  PersistentMemoryAllocator allocator_;
};

// Test parallel allocation/iteration and ensure consistency across all
// instances.
TEST_F(PersistentMemoryAllocatorTest, ParallelismTest) {
  void* memory = mem_segment_.get();
  AllocatorThread t1("t1", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
  AllocatorThread t2("t2", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
  AllocatorThread t3("t3", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
  AllocatorThread t4("t4", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
  AllocatorThread t5("t5", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);

  t1.Start();
  t2.Start();
  t3.Start();
  t4.Start();
  t5.Start();

  unsigned last_count = 0;
  do {
    unsigned count = CountIterables();
    EXPECT_LE(last_count, count);
  } while (!allocator_->IsCorrupt() && !allocator_->IsFull());

  t1.Join();
  t2.Join();
  t3.Join();
  t4.Join();
  t5.Join();

  EXPECT_FALSE(allocator_->IsCorrupt());
  EXPECT_TRUE(allocator_->IsFull());
  EXPECT_EQ(CountIterables(),
            t1.iterable() + t2.iterable() + t3.iterable() + t4.iterable() +
            t5.iterable());
}

// A simple thread that counts objects by iterating through an allocator.
class CounterThread : public SimpleThread {
 public:
  CounterThread(const std::string& name,
                PersistentMemoryAllocator::Iterator* iterator,
                Lock* lock,
                ConditionVariable* condition,
                bool* wake_up)
      : SimpleThread(name, Options()),
        iterator_(iterator),
        lock_(lock),
        condition_(condition),
        count_(0),
        wake_up_(wake_up) {}

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

  void Run() override {
    // Wait so all threads can start at approximately the same time.
    // Best performance comes from releasing a single worker which then
    // releases the next, etc., etc.
    {
      AutoLock autolock(*lock_);

      // Before calling Wait(), make sure that the wake up condition
      // has not already passed.  Also, since spurious signal events
      // are possible, check the condition in a while loop to make
      // sure that the wake up condition is met when this thread
      // returns from the Wait().
      // See usage comments in src/base/synchronization/condition_variable.h.
      while (!*wake_up_) {
        condition_->Wait();
        condition_->Signal();
      }
    }

    uint32_t type;
    while (iterator_->GetNext(&type) != 0) {
      ++count_;
    }
  }

  unsigned count() { return count_; }

 private:
  raw_ptr<PersistentMemoryAllocator::Iterator> iterator_;
  raw_ptr<Lock> lock_;
  raw_ptr<ConditionVariable> condition_;
  unsigned count_;
  raw_ptr<bool> wake_up_;
};

// Ensure that parallel iteration returns the same number of objects as
// single-threaded iteration.
TEST_F(PersistentMemoryAllocatorTest, IteratorParallelismTest) {
  // Fill the memory segment with random allocations.
  unsigned iterable_count = 0;
  for (;;) {
    uint32_t size = RandInt(1, 99);
    uint32_t type = RandInt(100, 999);
    Reference block = allocator_->Allocate(size, type);
    if (!block)
      break;
    allocator_->MakeIterable(block);
    ++iterable_count;
  }
  EXPECT_FALSE(allocator_->IsCorrupt());
  EXPECT_TRUE(allocator_->IsFull());
  EXPECT_EQ(iterable_count, CountIterables());

  PersistentMemoryAllocator::Iterator iter(allocator_.get());
  Lock lock;
  ConditionVariable condition(&lock);
  bool wake_up = false;

  CounterThread t1("t1", &iter, &lock, &condition, &wake_up);
  CounterThread t2("t2", &iter, &lock, &condition, &wake_up);
  CounterThread t3("t3", &iter, &lock, &condition, &wake_up);
  CounterThread t4("t4", &iter, &lock, &condition, &wake_up);
  CounterThread t5("t5", &iter, &lock, &condition, &wake_up);

  t1.Start();
  t2.Start();
  t3.Start();
  t4.Start();
  t5.Start();

  // Take the lock and set the wake up condition to true.  This helps to
  // avoid a race condition where the Signal() event is called before
  // all the threads have reached the Wait() and thus never get woken up.
  {
    AutoLock autolock(lock);
    wake_up = true;
  }

  // This will release all the waiting threads.
  condition.Signal();

  t1.Join();
  t2.Join();
  t3.Join();
  t4.Join();
  t5.Join();

  EXPECT_EQ(iterable_count,
            t1.count() + t2.count() + t3.count() + t4.count() + t5.count());

#if 0
  // These ensure that the threads don't run sequentially. It shouldn't be
  // enabled in general because it could lead to a flaky test if it happens
  // simply by chance but it is useful during development to ensure that the
  // test is working correctly.
  EXPECT_NE(iterable_count, t1.count());
  EXPECT_NE(iterable_count, t2.count());
  EXPECT_NE(iterable_count, t3.count());
  EXPECT_NE(iterable_count, t4.count());
  EXPECT_NE(iterable_count, t5.count());
#endif
}

TEST_F(PersistentMemoryAllocatorTest, DelayedAllocationTest) {
  std::atomic<Reference> ref1, ref2;
  ref1.store(0, std::memory_order_relaxed);
  ref2.store(0, std::memory_order_relaxed);
  DelayedPersistentAllocation da1(allocator_.get(), &ref1, 1001, 100, true);
  DelayedPersistentAllocation da2a(allocator_.get(), &ref2, 2002, 200, 0, true);
  DelayedPersistentAllocation da2b(allocator_.get(), &ref2, 2002, 200, 5, true);

  // Nothing should yet have been allocated.
  uint32_t type;
  PersistentMemoryAllocator::Iterator iter(allocator_.get());
  EXPECT_EQ(0U, iter.GetNext(&type));

  // Do first delayed allocation and check that a new persistent object exists.
  EXPECT_EQ(0U, da1.reference());
  void* mem1 = da1.Get();
  ASSERT_TRUE(mem1);
  EXPECT_NE(0U, da1.reference());
  EXPECT_EQ(allocator_->GetAsReference(mem1, 1001),
            ref1.load(std::memory_order_relaxed));
  EXPECT_NE(0U, iter.GetNext(&type));
  EXPECT_EQ(1001U, type);
  EXPECT_EQ(0U, iter.GetNext(&type));

  // Do second delayed allocation and check.
  void* mem2a = da2a.Get();
  ASSERT_TRUE(mem2a);
  EXPECT_EQ(allocator_->GetAsReference(mem2a, 2002),
            ref2.load(std::memory_order_relaxed));
  EXPECT_NE(0U, iter.GetNext(&type));
  EXPECT_EQ(2002U, type);
  EXPECT_EQ(0U, iter.GetNext(&type));

  // Third allocation should just return offset into second allocation.
  void* mem2b = da2b.Get();
  ASSERT_TRUE(mem2b);
  EXPECT_EQ(0U, iter.GetNext(&type));
  EXPECT_EQ(reinterpret_cast<uintptr_t>(mem2a) + 5,
            reinterpret_cast<uintptr_t>(mem2b));
}

// This test doesn't verify anything other than it doesn't crash. Its goal
// is to find coding errors that aren't otherwise tested for, much like a
// "fuzzer" would.
// This test is suppsoed to fail on TSAN bot (crbug.com/579867).
#if defined(THREAD_SANITIZER)
#define MAYBE_CorruptionTest DISABLED_CorruptionTest
#else
#define MAYBE_CorruptionTest CorruptionTest
#endif
TEST_F(PersistentMemoryAllocatorTest, MAYBE_CorruptionTest) {
  char* memory = mem_segment_.get();
  AllocatorThread t1("t1", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
  AllocatorThread t2("t2", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
  AllocatorThread t3("t3", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
  AllocatorThread t4("t4", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);
  AllocatorThread t5("t5", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE);

  t1.Start();
  t2.Start();
  t3.Start();
  t4.Start();
  t5.Start();

  do {
    size_t offset = RandInt(0, TEST_MEMORY_SIZE - 1);
    char value = RandInt(0, 255);
    memory[offset] = value;
  } while (!allocator_->IsCorrupt() && !allocator_->IsFull());

  t1.Join();
  t2.Join();
  t3.Join();
  t4.Join();
  t5.Join();

  CountIterables();
}

// Attempt to cause crashes or loops by expressly creating dangerous conditions.
TEST_F(PersistentMemoryAllocatorTest, MaliciousTest) {
  Reference block1 = allocator_->Allocate(sizeof(TestObject1), 1);
  Reference block2 = allocator_->Allocate(sizeof(TestObject1), 2);
  Reference block3 = allocator_->Allocate(sizeof(TestObject1), 3);
  Reference block4 = allocator_->Allocate(sizeof(TestObject1), 3);
  Reference block5 = allocator_->Allocate(sizeof(TestObject1), 3);
  allocator_->MakeIterable(block1);
  allocator_->MakeIterable(block2);
  allocator_->MakeIterable(block3);
  allocator_->MakeIterable(block4);
  allocator_->MakeIterable(block5);
  EXPECT_EQ(5U, CountIterables());
  EXPECT_FALSE(allocator_->IsCorrupt());

  // Create loop in iterable list and ensure it doesn't hang. The return value
  // from CountIterables() in these cases is unpredictable. If there is a
  // failure, the call will hang and the test killed for taking too long.
  uint32_t* header4 = (uint32_t*)(mem_segment_.get() + block4);
  EXPECT_EQ(block5, header4[3]);
  header4[3] = block4;
  CountIterables();  // loop: 1-2-3-4-4
  EXPECT_TRUE(allocator_->IsCorrupt());

  // Test where loop goes back to previous block.
  header4[3] = block3;
  CountIterables();  // loop: 1-2-3-4-3

  // Test where loop goes back to the beginning.
  header4[3] = block1;
  CountIterables();  // loop: 1-2-3-4-1
}

//----- LocalPersistentMemoryAllocator -----------------------------------------

TEST(LocalPersistentMemoryAllocatorTest, CreationTest) {
  LocalPersistentMemoryAllocator allocator(TEST_MEMORY_SIZE, 42, "");
  EXPECT_EQ(42U, allocator.Id());
  EXPECT_NE(0U, allocator.Allocate(24, 1));
  EXPECT_FALSE(allocator.IsFull());
  EXPECT_FALSE(allocator.IsCorrupt());
}

//----- {Writable,ReadOnly}SharedPersistentMemoryAllocator ---------------------

TEST(SharedPersistentMemoryAllocatorTest, CreationTest) {
  base::WritableSharedMemoryRegion rw_region =
      base::WritableSharedMemoryRegion::Create(TEST_MEMORY_SIZE);
  ASSERT_TRUE(rw_region.IsValid());

  PersistentMemoryAllocator::MemoryInfo meminfo1;
  Reference r123, r456, r789;
  {
    base::WritableSharedMemoryMapping mapping = rw_region.Map();
    ASSERT_TRUE(mapping.IsValid());
    WritableSharedPersistentMemoryAllocator local(std::move(mapping), TEST_ID,
                                                  "");
    EXPECT_FALSE(local.IsReadonly());
    r123 = local.Allocate(123, 123);
    r456 = local.Allocate(456, 456);
    r789 = local.Allocate(789, 789);
    local.MakeIterable(r123);
    local.ChangeType(r456, 654, 456, false);
    local.MakeIterable(r789);
    local.GetMemoryInfo(&meminfo1);
    EXPECT_FALSE(local.IsFull());
    EXPECT_FALSE(local.IsCorrupt());
  }

  // Create writable and read-only mappings of the same region.
  base::WritableSharedMemoryMapping rw_mapping = rw_region.Map();
  ASSERT_TRUE(rw_mapping.IsValid());
  base::ReadOnlySharedMemoryRegion ro_region =
      base::WritableSharedMemoryRegion::ConvertToReadOnly(std::move(rw_region));
  ASSERT_TRUE(ro_region.IsValid());
  base::ReadOnlySharedMemoryMapping ro_mapping = ro_region.Map();
  ASSERT_TRUE(ro_mapping.IsValid());

  // Read-only test.
  ReadOnlySharedPersistentMemoryAllocator shalloc2(std::move(ro_mapping), 0,
                                                   "");
  EXPECT_TRUE(shalloc2.IsReadonly());
  EXPECT_EQ(TEST_ID, shalloc2.Id());
  EXPECT_FALSE(shalloc2.IsFull());
  EXPECT_FALSE(shalloc2.IsCorrupt());

  PersistentMemoryAllocator::Iterator iter2(&shalloc2);
  uint32_t type;
  EXPECT_EQ(r123, iter2.GetNext(&type));
  EXPECT_EQ(r789, iter2.GetNext(&type));
  EXPECT_EQ(0U, iter2.GetNext(&type));

  EXPECT_EQ(123U, shalloc2.GetType(r123));
  EXPECT_EQ(654U, shalloc2.GetType(r456));
  EXPECT_EQ(789U, shalloc2.GetType(r789));

  PersistentMemoryAllocator::MemoryInfo meminfo2;
  shalloc2.GetMemoryInfo(&meminfo2);
  EXPECT_EQ(meminfo1.total, meminfo2.total);
  EXPECT_EQ(meminfo1.free, meminfo2.free);

  // Read/write test.
  WritableSharedPersistentMemoryAllocator shalloc3(std::move(rw_mapping), 0,
                                                   "");
  EXPECT_FALSE(shalloc3.IsReadonly());
  EXPECT_EQ(TEST_ID, shalloc3.Id());
  EXPECT_FALSE(shalloc3.IsFull());
  EXPECT_FALSE(shalloc3.IsCorrupt());

  PersistentMemoryAllocator::Iterator iter3(&shalloc3);
  EXPECT_EQ(r123, iter3.GetNext(&type));
  EXPECT_EQ(r789, iter3.GetNext(&type));
  EXPECT_EQ(0U, iter3.GetNext(&type));

  EXPECT_EQ(123U, shalloc3.GetType(r123));
  EXPECT_EQ(654U, shalloc3.GetType(r456));
  EXPECT_EQ(789U, shalloc3.GetType(r789));

  PersistentMemoryAllocator::MemoryInfo meminfo3;
  shalloc3.GetMemoryInfo(&meminfo3);
  EXPECT_EQ(meminfo1.total, meminfo3.total);
  EXPECT_EQ(meminfo1.free, meminfo3.free);

  // Interconnectivity test.
  Reference obj = shalloc3.Allocate(42, 42);
  ASSERT_TRUE(obj);
  shalloc3.MakeIterable(obj);
  EXPECT_EQ(obj, iter2.GetNext(&type));
  EXPECT_EQ(42U, type);

  // Clear-on-change test.
  Reference data_ref = shalloc3.Allocate(sizeof(int) * 4, 911);
  int* data = shalloc3.GetAsArray<int>(data_ref, 911, 4);
  ASSERT_TRUE(data);
  data[0] = 0;
  data[1] = 1;
  data[2] = 2;
  data[3] = 3;
  ASSERT_TRUE(shalloc3.ChangeType(data_ref, 119, 911, false));
  EXPECT_EQ(0, data[0]);
  EXPECT_EQ(1, data[1]);
  EXPECT_EQ(2, data[2]);
  EXPECT_EQ(3, data[3]);
  ASSERT_TRUE(shalloc3.ChangeType(data_ref, 191, 119, true));
  EXPECT_EQ(0, data[0]);
  EXPECT_EQ(0, data[1]);
  EXPECT_EQ(0, data[2]);
  EXPECT_EQ(0, data[3]);
}

#if !BUILDFLAG(IS_NACL)
//----- FilePersistentMemoryAllocator ------------------------------------------

TEST(FilePersistentMemoryAllocatorTest, CreationTest) {
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_path = temp_dir.GetPath().AppendASCII("persistent_memory");

  PersistentMemoryAllocator::MemoryInfo meminfo1;
  Reference r123, r456, r789;
  {
    LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, "");
    EXPECT_FALSE(local.IsReadonly());
    r123 = local.Allocate(123, 123);
    r456 = local.Allocate(456, 456);
    r789 = local.Allocate(789, 789);
    local.MakeIterable(r123);
    local.ChangeType(r456, 654, 456, false);
    local.MakeIterable(r789);
    local.GetMemoryInfo(&meminfo1);
    EXPECT_FALSE(local.IsFull());
    EXPECT_FALSE(local.IsCorrupt());

    File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
    ASSERT_TRUE(writer.IsValid());
    writer.Write(0, (const char*)local.data(), local.used());
  }

  std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
  ASSERT_TRUE(mmfile->Initialize(file_path));
  EXPECT_TRUE(mmfile->IsValid());
  const size_t mmlength = mmfile->length();
  EXPECT_GE(meminfo1.total, mmlength);

  FilePersistentMemoryAllocator file(std::move(mmfile), 0, 0, "", false);
  EXPECT_FALSE(file.IsReadonly());
  EXPECT_EQ(TEST_ID, file.Id());
  EXPECT_FALSE(file.IsFull());
  EXPECT_FALSE(file.IsCorrupt());

  PersistentMemoryAllocator::Iterator iter(&file);
  uint32_t type;
  EXPECT_EQ(r123, iter.GetNext(&type));
  EXPECT_EQ(r789, iter.GetNext(&type));
  EXPECT_EQ(0U, iter.GetNext(&type));

  EXPECT_EQ(123U, file.GetType(r123));
  EXPECT_EQ(654U, file.GetType(r456));
  EXPECT_EQ(789U, file.GetType(r789));

  PersistentMemoryAllocator::MemoryInfo meminfo2;
  file.GetMemoryInfo(&meminfo2);
  EXPECT_GE(meminfo1.total, meminfo2.total);
  EXPECT_GE(meminfo1.free, meminfo2.free);
  EXPECT_EQ(mmlength, meminfo2.total);
  EXPECT_EQ(0U, meminfo2.free);

  // There's no way of knowing if Flush actually does anything but at least
  // verify that it runs without CHECK violations.
  file.Flush(false);
  file.Flush(true);
}

TEST(FilePersistentMemoryAllocatorTest, ExtendTest) {
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_path = temp_dir.GetPath().AppendASCII("extend_test");
  MemoryMappedFile::Region region = {0, 16 << 10};  // 16KiB maximum size.

  // Start with a small but valid file of persistent data.
  ASSERT_FALSE(PathExists(file_path));
  {
    LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, "");
    local.Allocate(1, 1);
    local.Allocate(11, 11);

    File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
    ASSERT_TRUE(writer.IsValid());
    writer.Write(0, (const char*)local.data(), local.used());
  }
  ASSERT_TRUE(PathExists(file_path));
  int64_t before_size;
  ASSERT_TRUE(GetFileSize(file_path, &before_size));

  // Map it as an extendable read/write file and append to it.
  {
    std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
    ASSERT_TRUE(mmfile->Initialize(
        File(file_path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE),
        region, MemoryMappedFile::READ_WRITE_EXTEND));
    FilePersistentMemoryAllocator allocator(std::move(mmfile), region.size, 0,
                                            "", false);
    EXPECT_EQ(static_cast<size_t>(before_size), allocator.used());

    allocator.Allocate(111, 111);
    EXPECT_LT(static_cast<size_t>(before_size), allocator.used());
  }

  // Validate that append worked.
  int64_t after_size;
  ASSERT_TRUE(GetFileSize(file_path, &after_size));
  EXPECT_LT(before_size, after_size);

  // Verify that it's still an acceptable file.
  {
    std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
    ASSERT_TRUE(mmfile->Initialize(
        File(file_path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE),
        region, MemoryMappedFile::READ_WRITE_EXTEND));
    EXPECT_TRUE(FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true));
    EXPECT_TRUE(
        FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, false));
  }
}

TEST(FilePersistentMemoryAllocatorTest, AcceptableTest) {
  const uint32_t kAllocAlignment =
      PersistentMemoryAllocatorTest::GetAllocAlignment();
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());

  LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, "");
  local.MakeIterable(local.Allocate(1, 1));
  local.MakeIterable(local.Allocate(11, 11));
  const size_t minsize = local.used();
  std::unique_ptr<char[]> garbage(new char[minsize]);
  RandBytes(garbage.get(), minsize);

  std::unique_ptr<MemoryMappedFile> mmfile;
  char filename[100];
  for (size_t filesize = minsize; filesize > 0; --filesize) {
    strings::SafeSPrintf(filename, "memory_%d_A", filesize);
    FilePath file_path = temp_dir.GetPath().AppendASCII(filename);
    ASSERT_FALSE(PathExists(file_path));
    {
      File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
      ASSERT_TRUE(writer.IsValid());
      writer.Write(0, (const char*)local.data(), filesize);
    }
    ASSERT_TRUE(PathExists(file_path));

    // Request read/write access for some sizes that are a multple of the
    // allocator's alignment size. The allocator is strict about file size
    // being a multiple of its internal alignment when doing read/write access.
    const bool read_only = (filesize % (2 * kAllocAlignment)) != 0;
    const uint32_t file_flags =
        File::FLAG_OPEN | File::FLAG_READ | (read_only ? 0 : File::FLAG_WRITE);
    const MemoryMappedFile::Access map_access =
        read_only ? MemoryMappedFile::READ_ONLY : MemoryMappedFile::READ_WRITE;

    mmfile = std::make_unique<MemoryMappedFile>();
    ASSERT_TRUE(mmfile->Initialize(File(file_path, file_flags), map_access));
    EXPECT_EQ(filesize, mmfile->length());
    if (FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only)) {
      // Make sure construction doesn't crash. It will, however, cause
      // error messages warning about about a corrupted memory segment.
      FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "",
                                              read_only);
      // Also make sure that iteration doesn't crash.
      PersistentMemoryAllocator::Iterator iter(&allocator);
      uint32_t type_id;
      Reference ref;
      while ((ref = iter.GetNext(&type_id)) != 0) {
        const char* data = allocator.GetAsArray<char>(
            ref, 0, PersistentMemoryAllocator::kSizeAny);
        uint32_t type = allocator.GetType(ref);
        size_t size = allocator.GetAllocSize(ref);
        // Ensure compiler can't optimize-out above variables.
        (void)data;
        (void)type;
        (void)size;
      }

      // Ensure that short files are detected as corrupt and full files are not.
      EXPECT_EQ(filesize != minsize, allocator.IsCorrupt());
    } else {
      // For filesize >= minsize, the file must be acceptable. This
      // else clause (file-not-acceptable) should be reached only if
      // filesize < minsize.
      EXPECT_LT(filesize, minsize);
    }

    strings::SafeSPrintf(filename, "memory_%d_B", filesize);
    file_path = temp_dir.GetPath().AppendASCII(filename);
    ASSERT_FALSE(PathExists(file_path));
    {
      File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
      ASSERT_TRUE(writer.IsValid());
      writer.Write(0, (const char*)garbage.get(), filesize);
    }
    ASSERT_TRUE(PathExists(file_path));

    mmfile = std::make_unique<MemoryMappedFile>();
    ASSERT_TRUE(mmfile->Initialize(File(file_path, file_flags), map_access));
    EXPECT_EQ(filesize, mmfile->length());
    if (FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only)) {
      // Make sure construction doesn't crash. It will, however, cause
      // error messages warning about about a corrupted memory segment.
      FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "",
                                              read_only);
      EXPECT_TRUE(allocator.IsCorrupt());  // Garbage data so it should be.
    } else {
      // For filesize >= minsize, the file must be acceptable. This
      // else clause (file-not-acceptable) should be reached only if
      // filesize < minsize.
      EXPECT_GT(minsize, filesize);
    }
  }
}

TEST_F(PersistentMemoryAllocatorTest, TruncateTest) {
  ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  FilePath file_path = temp_dir.GetPath().AppendASCII("truncate_test");

  // Start with a small but valid file of persistent data. Keep the "used"
  // amount for both allocations.
  Reference a1_ref;
  Reference a2_ref;
  size_t a1_used;
  size_t a2_used;
  ASSERT_FALSE(PathExists(file_path));
  {
    LocalPersistentMemoryAllocator allocator(TEST_MEMORY_SIZE, TEST_ID, "");
    a1_ref = allocator.Allocate(100 << 10, 1);
    allocator.MakeIterable(a1_ref);
    a1_used = allocator.used();
    a2_ref = allocator.Allocate(200 << 10, 11);
    allocator.MakeIterable(a2_ref);
    a2_used = allocator.used();

    File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE);
    ASSERT_TRUE(writer.IsValid());
    writer.Write(0, static_cast<const char*>(allocator.data()),
                 allocator.size());
  }
  ASSERT_TRUE(PathExists(file_path));
  EXPECT_LE(a1_used, a2_ref);

  // Truncate the file to include everything and make sure it can be read, both
  // with read-write and read-only access.
  for (size_t file_length : {a2_used, a1_used, a1_used / 2}) {
    SCOPED_TRACE(StringPrintf("file_length=%zu", file_length));
    SetFileLength(file_path, file_length);

    for (bool read_only : {false, true}) {
      SCOPED_TRACE(StringPrintf("read_only=%s", read_only ? "true" : "false"));

      std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
      ASSERT_TRUE(mmfile->Initialize(
          File(file_path, File::FLAG_OPEN |
                              (read_only ? File::FLAG_READ
                                         : File::FLAG_READ | File::FLAG_WRITE)),
          read_only ? MemoryMappedFile::READ_ONLY
                    : MemoryMappedFile::READ_WRITE));
      ASSERT_TRUE(
          FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only));

      FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "",
                                              read_only);

      PersistentMemoryAllocator::Iterator iter(&allocator);
      uint32_t type_id;
      EXPECT_EQ(file_length >= a1_used ? a1_ref : 0U, iter.GetNext(&type_id));
      EXPECT_EQ(file_length >= a2_used ? a2_ref : 0U, iter.GetNext(&type_id));
      EXPECT_EQ(0U, iter.GetNext(&type_id));

      // Ensure that short files are detected as corrupt and full files are not.
      EXPECT_EQ(file_length < a2_used, allocator.IsCorrupt());
    }

    // Ensure that file length was not adjusted.
    int64_t actual_length;
    ASSERT_TRUE(GetFileSize(file_path, &actual_length));
    EXPECT_EQ(file_length, static_cast<size_t>(actual_length));
  }
}

#endif  // !BUILDFLAG(IS_NACL)

}  // namespace base


text:
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/public/common/loader/throttling_url_loader.h"

#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/url_request/redirect_util.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/url_response_head.mojom.h"

namespace blink {

namespace {

// Merges |removed_headers_B| into |removed_headers_A|.
void MergeRemovedHeaders(std::vector<std::string>* removed_headers_A,
                         const std::vector<std::string>& removed_headers_B) {
  for (auto& header : removed_headers_B) {
    if (!base::Contains(*removed_headers_A, header))
      removed_headers_A->emplace_back(std::move(header));
  }
}

}  // namespace

const char ThrottlingURLLoader::kFollowRedirectReason[] = "FollowRedirect";

class ThrottlingURLLoader::ForwardingThrottleDelegate
    : public URLLoaderThrottle::Delegate {
 public:
  ForwardingThrottleDelegate(ThrottlingURLLoader* loader,
                             URLLoaderThrottle* throttle)
      : loader_(loader), throttle_(throttle) {}
  ~ForwardingThrottleDelegate() override = default;

  // URLLoaderThrottle::Delegate:
  void CancelWithError(int error_code,
                       base::StringPiece custom_reason) override {
    if (!loader_)
      return;

    ScopedDelegateCall scoped_delegate_call(this);
    loader_->CancelWithError(error_code, custom_reason);
  }

  void Resume() override {
    if (!loader_)
      return;

    ScopedDelegateCall scoped_delegate_call(this);
    loader_->StopDeferringForThrottle(throttle_);
  }

  void SetPriority(net::RequestPriority priority) override {
    if (!loader_)
      return;

    ScopedDelegateCall scoped_delegate_call(this);
    loader_->SetPriority(priority);
  }

  void UpdateDeferredRequestHeaders(
      const net::HttpRequestHeaders& modified_request_headers,
      const net::HttpRequestHeaders& modified_cors_exempt_request_headers)
      override {
    if (!loader_)
      return;
    ScopedDelegateCall scoped_delegate_call(this);
    loader_->UpdateDeferredRequestHeaders(modified_request_headers,
                                          modified_cors_exempt_request_headers);
  }

  void UpdateDeferredResponseHead(
      network::mojom::URLResponseHeadPtr new_response_head) override {
    if (!loader_)
      return;
    ScopedDelegateCall scoped_delegate_call(this);
    loader_->UpdateDeferredResponseHead(std::move(new_response_head));
  }

  void PauseReadingBodyFromNet() override {
    if (!loader_)
      return;

    ScopedDelegateCall scoped_delegate_call(this);
    loader_->PauseReadingBodyFromNet(throttle_);
  }

  void ResumeReadingBodyFromNet() override {
    if (!loader_)
      return;

    ScopedDelegateCall scoped_delegate_call(this);
    loader_->ResumeReadingBodyFromNet(throttle_);
  }

  void InterceptResponse(
      mojo::PendingRemote<network::mojom::URLLoader> new_loader,
      mojo::PendingReceiver<network::mojom::URLLoaderClient>
          new_client_receiver,
      mojo::PendingRemote<network::mojom::URLLoader>* original_loader,
      mojo::PendingReceiver<network::mojom::URLLoaderClient>*
          original_client_receiver) override {
    if (!loader_)
      return;

    ScopedDelegateCall scoped_delegate_call(this);
    loader_->InterceptResponse(std::move(new_loader),
                               std::move(new_client_receiver), original_loader,
                               original_client_receiver);
  }

  void RestartWithFlags(int additional_load_flags) override {
    if (!loader_)
      return;

    ScopedDelegateCall scoped_delegate_call(this);
    loader_->RestartWithFlags(additional_load_flags);
  }

  void RestartWithURLResetAndFlags(int additional_load_flags) override {
    if (!loader_)
      return;

    ScopedDelegateCall scoped_delegate_call(this);
    loader_->RestartWithURLResetAndFlags(additional_load_flags);
  }

  void Detach() { loader_ = nullptr; }

 private:
  // This class helps ThrottlingURLLoader to keep track of whether it is being
  // called by its throttles.
  // If ThrottlingURLLoader is destoyed while any of the throttles is calling
  // into it, it delays destruction of the throttles. That way throttles don't
  // need to worry about any delegate calls may destory them synchronously.
  class ScopedDelegateCall {
   public:
    explicit ScopedDelegateCall(ForwardingThrottleDelegate* owner)
        : owner_(owner) {
      DCHECK(owner_->loader_);

      owner_->loader_->inside_delegate_calls_++;
    }

    ~ScopedDelegateCall() {
      // The loader may have been detached and destroyed.
      if (owner_->loader_)
        owner_->loader_->inside_delegate_calls_--;
    }

   private:
    ForwardingThrottleDelegate* const owner_;
    DISALLOW_COPY_AND_ASSIGN(ScopedDelegateCall);
  };

  ThrottlingURLLoader* loader_;
  URLLoaderThrottle* const throttle_;

  DISALLOW_COPY_AND_ASSIGN(ForwardingThrottleDelegate);
};

ThrottlingURLLoader::StartInfo::StartInfo(
    scoped_refptr<network::SharedURLLoaderFactory> in_url_loader_factory,
    int32_t in_routing_id,
    int32_t in_request_id,
    uint32_t in_options,
    network::ResourceRequest* in_url_request,
    scoped_refptr<base::SingleThreadTaskRunner> in_task_runner)
    : url_loader_factory(std::move(in_url_loader_factory)),
      routing_id(in_routing_id),
      request_id(in_request_id),
      options(in_options),
      url_request(*in_url_request),
      task_runner(std::move(in_task_runner)) {}

ThrottlingURLLoader::StartInfo::~StartInfo() = default;

ThrottlingURLLoader::ResponseInfo::ResponseInfo(
    network::mojom::URLResponseHeadPtr in_response_head)
    : response_head(std::move(in_response_head)) {}

ThrottlingURLLoader::ResponseInfo::~ResponseInfo() = default;

ThrottlingURLLoader::RedirectInfo::RedirectInfo(
    const net::RedirectInfo& in_redirect_info,
    network::mojom::URLResponseHeadPtr in_response_head)
    : redirect_info(in_redirect_info),
      response_head(std::move(in_response_head)) {}

ThrottlingURLLoader::RedirectInfo::~RedirectInfo() = default;

ThrottlingURLLoader::PriorityInfo::PriorityInfo(
    net::RequestPriority in_priority,
    int32_t in_intra_priority_value)
    : priority(in_priority), intra_priority_value(in_intra_priority_value) {}

// static
std::unique_ptr<ThrottlingURLLoader> ThrottlingURLLoader::CreateLoaderAndStart(
    scoped_refptr<network::SharedURLLoaderFactory> factory,
    std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
    int32_t routing_id,
    int32_t request_id,
    uint32_t options,
    network::ResourceRequest* url_request,
    network::mojom::URLLoaderClient* client,
    const net::NetworkTrafficAnnotationTag& traffic_annotation,
    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
  std::unique_ptr<ThrottlingURLLoader> loader(new ThrottlingURLLoader(
      std::move(throttles), client, traffic_annotation));
  loader->Start(std::move(factory), routing_id, request_id, options,
                url_request, std::move(task_runner));
  return loader;
}

ThrottlingURLLoader::~ThrottlingURLLoader() {
  if (inside_delegate_calls_ > 0) {
    // A throttle is calling into this object. In this case, delay destruction
    // of the throttles, so that throttles don't need to worry about any
    // delegate calls may destory them synchronously.
    for (auto& entry : throttles_)
      entry.delegate->Detach();

    auto throttles =
        std::make_unique<std::vector<ThrottleEntry>>(std::move(throttles_));
    base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
                                                    std::move(throttles));
  }
}

void ThrottlingURLLoader::FollowRedirectForcingRestart() {
  ResetForFollowRedirect();
  client_receiver_.reset();
  CHECK(throttle_will_redirect_redirect_url_.is_empty());

  for (const std::string& header : removed_headers_) {
    start_info_->url_request.headers.RemoveHeader(header);
    start_info_->url_request.cors_exempt_headers.RemoveHeader(header);
  }
  start_info_->url_request.headers.MergeFrom(modified_headers_);
  start_info_->url_request.cors_exempt_headers.MergeFrom(
      modified_cors_exempt_headers_);

  removed_headers_.clear();
  modified_headers_.Clear();
  modified_cors_exempt_headers_.Clear();

  StartNow();
}

void ThrottlingURLLoader::ResetForFollowRedirect() {
  url_loader_.ResetWithReason(
      network::mojom::URLLoader::kClientDisconnectReason,
      kFollowRedirectReason);
}

void ThrottlingURLLoader::RestartWithFactory(
    scoped_refptr<network::SharedURLLoaderFactory> factory,
    uint32_t url_loader_options) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);
  url_loader_.reset();
  client_receiver_.reset();
  start_info_->url_loader_factory = std::move(factory);
  start_info_->options = url_loader_options;
  StartNow();
}

void ThrottlingURLLoader::FollowRedirect(
    const std::vector<std::string>& removed_headers,
    const net::HttpRequestHeaders& modified_headers,
    const net::HttpRequestHeaders& modified_cors_exempt_headers) {
  MergeRemovedHeaders(&removed_headers_, removed_headers);
  modified_headers_.MergeFrom(modified_headers);
  modified_cors_exempt_headers_.MergeFrom(modified_cors_exempt_headers);

  if (!throttle_will_start_redirect_url_.is_empty()) {
    throttle_will_start_redirect_url_ = GURL();
    // This is a synthesized redirect, so no need to tell the URLLoader.
    StartNow();
    return;
  }

  if (url_loader_) {
    base::Optional<GURL> new_url;
    if (!throttle_will_redirect_redirect_url_.is_empty())
      new_url = throttle_will_redirect_redirect_url_;
    url_loader_->FollowRedirect(removed_headers_, modified_headers_,
                                modified_cors_exempt_headers_, new_url);
    throttle_will_redirect_redirect_url_ = GURL();
  }

  removed_headers_.clear();
  modified_headers_.Clear();
  modified_cors_exempt_headers_.Clear();
}

void ThrottlingURLLoader::SetPriority(net::RequestPriority priority,
                                      int32_t intra_priority_value) {
  if (!url_loader_) {
    if (!loader_completed_) {
      // Only check |deferred_stage_| if this resource has not been redirected
      // by a throttle.
      if (throttle_will_start_redirect_url_.is_empty() &&
          throttle_will_redirect_redirect_url_.is_empty()) {
        DCHECK_EQ(DEFERRED_START, deferred_stage_);
      }

      priority_info_ =
          std::make_unique<PriorityInfo>(priority, intra_priority_value);
    }
    return;
  }

  url_loader_->SetPriority(priority, intra_priority_value);
}

void ThrottlingURLLoader::PauseReadingBodyFromNet() {
  PauseReadingBodyFromNet(/*throttle=*/nullptr);
}

void ThrottlingURLLoader::ResumeReadingBodyFromNet() {
  ResumeReadingBodyFromNet(/*throttle=*/nullptr);
}

network::mojom::URLLoaderClientEndpointsPtr ThrottlingURLLoader::Unbind() {
  return network::mojom::URLLoaderClientEndpoints::New(
      url_loader_.Unbind(), client_receiver_.Unbind());
}

ThrottlingURLLoader::ThrottlingURLLoader(
    std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
    network::mojom::URLLoaderClient* client,
    const net::NetworkTrafficAnnotationTag& traffic_annotation)
    : forwarding_client_(client), traffic_annotation_(traffic_annotation) {
  throttles_.reserve(throttles.size());
  for (auto& throttle : throttles)
    throttles_.emplace_back(this, std::move(throttle));
}

void ThrottlingURLLoader::Start(
    scoped_refptr<network::SharedURLLoaderFactory> factory,
    int32_t routing_id,
    int32_t request_id,
    uint32_t options,
    network::ResourceRequest* url_request,
    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);

  bool deferred = false;
  DCHECK(deferring_throttles_.empty());
  if (!throttles_.empty()) {
    original_url_ = url_request->url;
    for (auto& entry : throttles_) {
      auto* throttle = entry.throttle.get();
      bool throttle_deferred = false;
      throttle->WillStartRequest(url_request, &throttle_deferred);
      if (original_url_ != url_request->url) {
        DCHECK(throttle_will_start_redirect_url_.is_empty())
            << "ThrottlingURLLoader doesn't support multiple throttles "
               "changing the URL.";
        if (original_url_.SchemeIsHTTPOrHTTPS() &&
            !url_request->url.SchemeIsHTTPOrHTTPS() &&
            !throttle->makes_unsafe_redirect()) {
          NOTREACHED() << "A URLLoaderThrottle can't redirect from http(s) to "
                       << "a non http(s) scheme.";
        } else {
          throttle_will_start_redirect_url_ = url_request->url;
        }
        // Restore the original URL so that all throttles see the same original
        // URL.
        url_request->url = original_url_;
      }
      if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
        return;
    }
  }

  start_info_ =
      std::make_unique<StartInfo>(factory, routing_id, request_id, options,
                                  url_request, std::move(task_runner));
  if (deferred)
    deferred_stage_ = DEFERRED_START;
  else
    StartNow();
}

void ThrottlingURLLoader::StartNow() {
  DCHECK(start_info_);
  if (throttle_will_start_original_url_) {
    throttle_will_start_redirect_url_ = original_url_;
    throttle_will_start_original_url_ = false;
  }
  if (!throttle_will_start_redirect_url_.is_empty()) {
    auto first_party_url_policy =
        start_info_->url_request.update_first_party_url_on_redirect
            ? net::URLRequest::FirstPartyURLPolicy::
                  UPDATE_FIRST_PARTY_URL_ON_REDIRECT
            : net::URLRequest::FirstPartyURLPolicy::
                  NEVER_CHANGE_FIRST_PARTY_URL;

    net::RedirectInfo redirect_info = net::RedirectInfo::ComputeRedirectInfo(
        start_info_->url_request.method, start_info_->url_request.url,
        start_info_->url_request.site_for_cookies, first_party_url_policy,
        start_info_->url_request.referrer_policy,
        start_info_->url_request.referrer.spec(),
        // Use status code 307 to preserve the method, so POST requests work.
        net::HTTP_TEMPORARY_REDIRECT, throttle_will_start_redirect_url_,
        base::nullopt, false, false, false);

    bool should_clear_upload = false;
    net::RedirectUtil::UpdateHttpRequest(
        start_info_->url_request.url, start_info_->url_request.method,
        redirect_info, base::nullopt, base::nullopt,
        &start_info_->url_request.headers, &should_clear_upload);

    if (should_clear_upload)
      start_info_->url_request.request_body = nullptr;

    // Set the new URL in the ResourceRequest struct so that it is the URL
    // that's requested.
    start_info_->url_request.url = throttle_will_start_redirect_url_;

    auto response_head = network::mojom::URLResponseHead::New();
    std::string header_string = base::StringPrintf(
        "HTTP/1.1 %i Internal Redirect\n"
        "Location: %s",
        net::HTTP_TEMPORARY_REDIRECT,
        throttle_will_start_redirect_url_.spec().c_str());

    // This is only needed when CORS is running in the renderer.
    // TODO(crbug.com/1001450): Remove following code once OOR-CORS is fully
    // enabled.
    std::string http_origin;
    if (start_info_->url_request.headers.GetHeader("Origin", &http_origin)) {
      // If this redirect is used in a cross-origin request, add CORS headers
      // to make sure that the redirect gets through. Note that the
      // destination URL is still subject to the usual CORS policy, i.e. the
      // resource will only be available to web pages if the server serves the
      // response with the required CORS response headers.
      header_string += base::StringPrintf(
          "\n"
          "Access-Control-Allow-Origin: %s\n"
          "Access-Control-Allow-Credentials: true",
          http_origin.c_str());
    }

    response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
        net::HttpUtil::AssembleRawHeaders(header_string));
    response_head->encoded_data_length = header_string.size();
    start_info_->task_runner->PostTask(
        FROM_HERE,
        base::BindOnce(&ThrottlingURLLoader::OnReceiveRedirect,
                       weak_factory_.GetWeakPtr(), std::move(redirect_info),
                       std::move(response_head)));
    return;
  }

  DCHECK(start_info_->url_loader_factory);
  start_info_->url_loader_factory->CreateLoaderAndStart(
      url_loader_.BindNewPipeAndPassReceiver(), start_info_->routing_id,
      start_info_->request_id, start_info_->options, start_info_->url_request,
      client_receiver_.BindNewPipeAndPassRemote(start_info_->task_runner),
      net::MutableNetworkTrafficAnnotationTag(traffic_annotation_));

  // TODO(https://crbug.com/919736): Remove this call.
  client_receiver_.internal_state()->EnableBatchDispatch();

  client_receiver_.set_disconnect_handler(base::BindOnce(
      &ThrottlingURLLoader::OnClientConnectionError, base::Unretained(this)));

  if (!pausing_reading_body_from_net_throttles_.empty())
    url_loader_->PauseReadingBodyFromNet();

  if (priority_info_) {
    auto priority_info = std::move(priority_info_);
    url_loader_->SetPriority(priority_info->priority,
                             priority_info->intra_priority_value);
  }

  // Initialize with the request URL, may be updated when on redirects
  response_url_ = start_info_->url_request.url;
}

void ThrottlingURLLoader::RestartWithFlagsNow() {
  DCHECK(has_pending_restart_);
  url_loader_.reset();
  client_receiver_.reset();
  start_info_->url_request.load_flags |= pending_restart_flags_;
  has_pending_restart_ = false;
  pending_restart_flags_ = 0;
  StartNow();
}

bool ThrottlingURLLoader::HandleThrottleResult(URLLoaderThrottle* throttle,
                                               bool throttle_deferred,
                                               bool* should_defer) {
  DCHECK(!deferring_throttles_.count(throttle));
  if (loader_completed_)
    return false;
  *should_defer |= throttle_deferred;
  if (throttle_deferred)
    deferring_throttles_.insert(throttle);
  return true;
}

void ThrottlingURLLoader::StopDeferringForThrottle(
    URLLoaderThrottle* throttle) {
  if (deferring_throttles_.find(throttle) == deferring_throttles_.end())
    return;

  deferring_throttles_.erase(throttle);
  if (deferring_throttles_.empty() && !loader_completed_)
    Resume();
}

void ThrottlingURLLoader::RestartWithFlags(int additional_load_flags) {
  pending_restart_flags_ |= additional_load_flags;
  has_pending_restart_ = true;
}

void ThrottlingURLLoader::RestartWithURLResetAndFlags(
    int additional_load_flags) {
  pending_restart_flags_ |= additional_load_flags;
  throttle_will_start_original_url_ = true;
  has_pending_restart_ = true;
}

void ThrottlingURLLoader::OnReceiveResponse(
    network::mojom::URLResponseHeadPtr response_head) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);
  DCHECK(deferring_throttles_.empty());
  TRACE_EVENT1("loading", "ThrottlingURLLoader::OnReceiveResponse", "url",
               response_url_.possibly_invalid_spec());

  // Dispatch BeforeWillProcessResponse().
  if (!throttles_.empty()) {
    pending_restart_flags_ = 0;
    has_pending_restart_ = false;
    bool deferred = false;
    for (auto& entry : throttles_) {
      auto* throttle = entry.throttle.get();
      bool throttle_deferred = false;
      throttle->BeforeWillProcessResponse(response_url_, *response_head,
                                          &throttle_deferred);
      if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
        return;
    }

    if (deferred) {
      deferred_stage_ = DEFERRED_BEFORE_RESPONSE;
      client_receiver_.Pause();
      return;
    }

    if (has_pending_restart_) {
      RestartWithFlagsNow();
      return;
    }
  }

  // Dispatch WillProcessResponse().
  if (!throttles_.empty()) {
    bool deferred = false;
    for (auto& entry : throttles_) {
      auto* throttle = entry.throttle.get();
      bool throttle_deferred = false;
      throttle->WillProcessResponse(response_url_, response_head.get(),
                                    &throttle_deferred);
      if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
        return;
    }

    if (deferred) {
      deferred_stage_ = DEFERRED_RESPONSE;
      response_info_ = std::make_unique<ResponseInfo>(std::move(response_head));
      client_receiver_.Pause();
      return;
    }
  }

  forwarding_client_->OnReceiveResponse(std::move(response_head));
}

void ThrottlingURLLoader::OnReceiveRedirect(
    const net::RedirectInfo& redirect_info,
    network::mojom::URLResponseHeadPtr response_head) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);
  DCHECK(deferring_throttles_.empty());

  if (!throttles_.empty()) {
    bool deferred = false;
    for (auto& entry : throttles_) {
      auto* throttle = entry.throttle.get();
      bool throttle_deferred = false;
      auto weak_ptr = weak_factory_.GetWeakPtr();
      std::vector<std::string> removed_headers;
      net::HttpRequestHeaders modified_headers;
      net::HttpRequestHeaders modified_cors_exempt_headers;
      net::RedirectInfo redirect_info_copy = redirect_info;
      throttle->WillRedirectRequest(
          &redirect_info_copy, *response_head, &throttle_deferred,
          &removed_headers, &modified_headers, &modified_cors_exempt_headers);
      if (redirect_info_copy.new_url != redirect_info.new_url) {
        DCHECK(throttle_will_redirect_redirect_url_.is_empty())
            << "ThrottlingURLLoader doesn't support multiple throttles "
               "changing the URL.";
        throttle_will_redirect_redirect_url_ = redirect_info_copy.new_url;
      }

      if (!weak_ptr)
        return;
      if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
        return;

      MergeRemovedHeaders(&removed_headers_, removed_headers);
      modified_headers_.MergeFrom(modified_headers);
      modified_cors_exempt_headers_.MergeFrom(modified_cors_exempt_headers);
    }

    if (deferred) {
      deferred_stage_ = DEFERRED_REDIRECT;
      redirect_info_ = std::make_unique<RedirectInfo>(redirect_info,
                                                      std::move(response_head));
      // |client_receiver_| can be unbound if the redirect came from a
      // throttle.
      if (client_receiver_.is_bound())
        client_receiver_.Pause();
      return;
    }
  }

  // Update the request in case |FollowRedirectForcingRestart()| is called, and
  // needs to use the request updated for the redirect.
  network::ResourceRequest& request = start_info_->url_request;
  request.url = redirect_info.new_url;
  request.method = redirect_info.new_method;
  request.site_for_cookies = redirect_info.new_site_for_cookies;
  request.referrer = GURL(redirect_info.new_referrer);
  request.referrer_policy = redirect_info.new_referrer_policy;

  // TODO(dhausknecht) at this point we do not actually know if we commit to the
  // redirect or if it will be cancelled. FollowRedirect would be a more
  // suitable place to set this URL but there we do not have the data.
  response_url_ = redirect_info.new_url;
  forwarding_client_->OnReceiveRedirect(redirect_info,
                                        std::move(response_head));
}

void ThrottlingURLLoader::OnUploadProgress(
    int64_t current_position,
    int64_t total_size,
    OnUploadProgressCallback ack_callback) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);

  forwarding_client_->OnUploadProgress(current_position, total_size,
                                       std::move(ack_callback));
}

void ThrottlingURLLoader::OnReceiveCachedMetadata(mojo_base::BigBuffer data) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);

  forwarding_client_->OnReceiveCachedMetadata(std::move(data));
}

void ThrottlingURLLoader::OnTransferSizeUpdated(int32_t transfer_size_diff) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);

  forwarding_client_->OnTransferSizeUpdated(transfer_size_diff);
}

void ThrottlingURLLoader::OnStartLoadingResponseBody(
    mojo::ScopedDataPipeConsumerHandle body) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);
  TRACE_EVENT1("loading", "ThrottlingURLLoader::OnStartLoadingResponseBody",
               "url", response_url_.possibly_invalid_spec());

  forwarding_client_->OnStartLoadingResponseBody(std::move(body));
}

void ThrottlingURLLoader::OnComplete(
    const network::URLLoaderCompletionStatus& status) {
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);

  // Only dispatch WillOnCompleteWithError() if status is not OK.
  if (!throttles_.empty() && status.error_code != net::OK) {
    pending_restart_flags_ = 0;
    has_pending_restart_ = false;
    bool deferred = false;
    for (auto& entry : throttles_) {
      auto* throttle = entry.throttle.get();
      bool throttle_deferred = false;
      throttle->WillOnCompleteWithError(status, &throttle_deferred);
      if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
        return;
    }

    if (deferred) {
      deferred_stage_ = DEFERRED_COMPLETE;
      client_receiver_.Pause();
      return;
    }

    if (has_pending_restart_) {
      RestartWithFlagsNow();
      return;
    }
  }

  // This is the last expected message. Pipe closure before this is an error
  // (see OnClientConnectionError). After this it is expected and should be
  // ignored. The owner of |this| is expected to destroy |this| when
  // OnComplete() and all data has been read. Destruction of |this| will
  // destroy |url_loader_| appropriately.
  loader_completed_ = true;
  forwarding_client_->OnComplete(status);
}

void ThrottlingURLLoader::OnClientConnectionError() {
  CancelWithError(net::ERR_ABORTED, "");
}

void ThrottlingURLLoader::CancelWithError(int error_code,
                                          base::StringPiece custom_reason) {
  if (loader_completed_)
    return;

  network::URLLoaderCompletionStatus status;
  status.error_code = error_code;
  status.completion_time = base::TimeTicks::Now();

  deferred_stage_ = DEFERRED_NONE;
  DisconnectClient(custom_reason);
  forwarding_client_->OnComplete(status);
}

void ThrottlingURLLoader::Resume() {
  if (loader_completed_ || deferred_stage_ == DEFERRED_NONE)
    return;

  auto prev_deferred_stage = deferred_stage_;
  deferred_stage_ = DEFERRED_NONE;
  switch (prev_deferred_stage) {
    case DEFERRED_START: {
      StartNow();
      break;
    }
    case DEFERRED_REDIRECT: {
      // |client_receiver_| can be unbound if the redirect came from a
      // throttle.
      if (client_receiver_.is_bound())
        client_receiver_.Resume();
      // TODO(dhausknecht) at this point we do not actually know if we commit to
      // the redirect or if it will be cancelled. FollowRedirect would be a more
      // suitable place to set this URL but there we do not have the data.
      response_url_ = redirect_info_->redirect_info.new_url;
      forwarding_client_->OnReceiveRedirect(
          redirect_info_->redirect_info,
          std::move(redirect_info_->response_head));
      // Note: |this| may be deleted here.
      break;
    }
    case DEFERRED_BEFORE_RESPONSE: {
      // TODO(eroman): For simplicity we require throttles that defer during
      // BeforeWillProcessResponse() to do a restart. We could support deferring
      // and choosing not to restart if needed, however the current consumers
      // don't need that.
      CHECK(has_pending_restart_);

      RestartWithFlagsNow();
      // Note: |this| may be deleted here.
      break;
    }
    case DEFERRED_RESPONSE: {
      client_receiver_.Resume();
      forwarding_client_->OnReceiveResponse(
          std::move(response_info_->response_head));
      // Note: |this| may be deleted here.
      break;
    }
    case DEFERRED_COMPLETE: {
      // TODO(eroman): For simplicity we require throttles that defer during
      // WillOnCompleteWithError() to do a restart. We could support deferring
      // and choosing not to restart if needed, however the current consumers
      // don't need that.
      CHECK(has_pending_restart_);

      RestartWithFlagsNow();
      // Note: |this| may be deleted here.
      break;
    }
    default:
      NOTREACHED();
      break;
  }
}

void ThrottlingURLLoader::SetPriority(net::RequestPriority priority) {
  if (url_loader_)
    url_loader_->SetPriority(priority, -1);
}

void ThrottlingURLLoader::UpdateDeferredRequestHeaders(
    const net::HttpRequestHeaders& modified_request_headers,
    const net::HttpRequestHeaders& modified_cors_exempt_request_headers) {
  if (deferred_stage_ == DEFERRED_START) {
    start_info_->url_request.headers.MergeFrom(modified_request_headers);
    start_info_->url_request.cors_exempt_headers.MergeFrom(
        modified_cors_exempt_request_headers);
  } else if (deferred_stage_ == DEFERRED_REDIRECT) {
    modified_headers_.MergeFrom(modified_request_headers);
    modified_cors_exempt_headers_.MergeFrom(
        modified_cors_exempt_request_headers);
  } else {
    NOTREACHED()
        << "Can only update headers of a request before it's sent out.";
  }
}

void ThrottlingURLLoader::UpdateDeferredResponseHead(
    network::mojom::URLResponseHeadPtr new_response_head) {
  DCHECK(response_info_);
  DCHECK_EQ(DEFERRED_RESPONSE, deferred_stage_);
  response_info_->response_head = std::move(new_response_head);
}

void ThrottlingURLLoader::PauseReadingBodyFromNet(URLLoaderThrottle* throttle) {
  if (pausing_reading_body_from_net_throttles_.empty() && url_loader_)
    url_loader_->PauseReadingBodyFromNet();

  pausing_reading_body_from_net_throttles_.insert(throttle);
}

void ThrottlingURLLoader::ResumeReadingBodyFromNet(
    URLLoaderThrottle* throttle) {
  auto iter = pausing_reading_body_from_net_throttles_.find(throttle);
  if (iter == pausing_reading_body_from_net_throttles_.end())
    return;

  pausing_reading_body_from_net_throttles_.erase(iter);
  if (pausing_reading_body_from_net_throttles_.empty() && url_loader_)
    url_loader_->ResumeReadingBodyFromNet();
}

void ThrottlingURLLoader::InterceptResponse(
    mojo::PendingRemote<network::mojom::URLLoader> new_loader,
    mojo::PendingReceiver<network::mojom::URLLoaderClient> new_client_receiver,
    mojo::PendingRemote<network::mojom::URLLoader>* original_loader,
    mojo::PendingReceiver<network::mojom::URLLoaderClient>*
        original_client_receiver) {
  response_intercepted_ = true;

  if (original_loader)
    *original_loader = url_loader_.Unbind();
  url_loader_.Bind(std::move(new_loader));

  if (original_client_receiver)
    *original_client_receiver = client_receiver_.Unbind();
  client_receiver_.Bind(std::move(new_client_receiver),
                        start_info_->task_runner);
  client_receiver_.set_disconnect_handler(base::BindOnce(
      &ThrottlingURLLoader::OnClientConnectionError, base::Unretained(this)));
}

void ThrottlingURLLoader::DisconnectClient(base::StringPiece custom_reason) {
  client_receiver_.reset();

  if (!custom_reason.empty()) {
    url_loader_.ResetWithReason(
        network::mojom::URLLoader::kClientDisconnectReason,
        custom_reason.as_string());
  } else {
    url_loader_.reset();
  }

  loader_completed_ = true;
}

ThrottlingURLLoader::ThrottleEntry::ThrottleEntry(
    ThrottlingURLLoader* loader,
    std::unique_ptr<URLLoaderThrottle> the_throttle)
    : delegate(
          std::make_unique<ForwardingThrottleDelegate>(loader,
                                                       the_throttle.get())),
      throttle(std::move(the_throttle)) {
  throttle->set_delegate(delegate.get());
}

ThrottlingURLLoader::ThrottleEntry::ThrottleEntry(ThrottleEntry&& other) =
    default;

ThrottlingURLLoader::ThrottleEntry::~ThrottleEntry() = default;

ThrottlingURLLoader::ThrottleEntry& ThrottlingURLLoader::ThrottleEntry::
operator=(ThrottleEntry&& other) = default;

}  // namespace blink

redpajama_set_name: RedPajamaGithub


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
in Stchigel, Guarro & Mac Cormack, Mycologia 95(6): 1219 (2003)

#### Original name
Apiosordaria antarctica Stchigel & Guarro

### Remarks
null


text:
require_relative "../../lib/core_ext/integer"

RSpec.describe "String extensions" do
  describe "#comma_separated" do
    it "converts to comma separated string" do
      expect(0.comma_separated).to eq "0"
      expect(23.comma_separated).to eq "23"
      expect(134.comma_separated).to eq "134"
      expect(1234.comma_separated).to eq "1,234"
      expect(48955.comma_separated).to eq "48,955"
      expect(1_000_000.comma_separated).to eq "1,000,000"
    end
  end
end


text:
description: This template creates a Windows VM and sets up a trial version of McAfee Endpoint Security
page_type: sample
products:
- azure
- azure-resource-manager
urlFragment: mcafee-extension-windows-vm
languages:
- json
---
# McAfee Endpoint Security (trial license) on Windows VM

![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/demos/mcafee-extension-windows-vm/PublicLastTestDate.svg)
![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/demos/mcafee-extension-windows-vm/PublicDeployment.svg)

![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/demos/mcafee-extension-windows-vm/FairfaxLastTestDate.svg)
![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/demos/mcafee-extension-windows-vm/FairfaxDeployment.svg)

![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/demos/mcafee-extension-windows-vm/BestPracticeResult.svg)
![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/demos/mcafee-extension-windows-vm/CredScanResult.svg)

[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fdemos%2Fmcafee-extension-windows-vm%2Fazuredeploy.json)  [![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fdemos%2Fmcafee-extension-windows-vm%2Fazuredeploy.json)

Create a Windows VM with the McAfee Endpoint Security extension enabled. This is a sample template that shows how to set up a VM with a trial version of the McAfee Endpoint Security extension.

`Tags: Microsoft.Storage/storageAccounts, Microsoft.Network/publicIPAddresses, Microsoft.Network/virtualNetworks, Microsoft.Network/networkInterfaces, Microsoft.Compute/virtualMachines, Microsoft.Compute/virtualMachines/extensions, McAfeeEndpointSecurity`


text:
/*
*******************************************************************************
*   BTChip Bitcoin Hardware Wallet Java Card implementation
*   (c) 2013 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU Affero General Public License as
*   published by the Free Software Foundation, either version 3 of the
*   License, or (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU Affero General Public License for more details.
*
*   You should have received a copy of the GNU Affero General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************
*/

package com.btchip.applet.poc;

import javacard.framework.Util;
import javacard.security.CryptoException;
import javacard.security.ECKey;
import javacard.security.ECPrivateKey;
import javacard.security.KeyBuilder;
import javacard.security.KeyPair;
import javacard.security.MessageDigest;
import javacard.security.RandomData;
import javacard.security.Signature;
import javacardx.crypto.Cipher;

/**
 * Bitcoin signature implementation
 * @author BTChip
 *
 */
public class Crypto {

    public static void init() {
        random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
        // it's not even possible to request a transient pair, yay.
        generatingPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
        try {
            // ok, let's save RAM
            transientPrivate = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE_TRANSIENT_DESELECT, KeyBuilder.LENGTH_EC_FP_256, false);
            transientPrivateTransient = true;
        }
        catch(CryptoException e) {
            try {
                // ok, let's save a bit less RAM
                transientPrivate = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE_TRANSIENT_RESET, KeyBuilder.LENGTH_EC_FP_256, false);
                transientPrivateTransient = true;
            }
            catch(CryptoException e1) {
                // ok, let's test the flash wear leveling \o/
                transientPrivate = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false);
                initKeyCurve(transientPrivate);
            }
        }
        initKeyCurve((ECKey)generatingPair.getPublic());
        initKeyCurve((ECKey)generatingPair.getPrivate());
        digestFull = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
        digestAuthorization = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
        digestScratch = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
        blobEncryptDecrypt = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);
        signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
        try {
            digestRipemd = MessageDigest.getInstance(MessageDigest.ALG_RIPEMD160, false);
        }
        catch(CryptoException e) {
            // A typical Java Card implementation will not support RIPEMD160 - we deal with it
            Ripemd160.init();
        }
    }

    public static KeyPair generatePair() {
        generatingPair.genKeyPair();
        return generatingPair;
    }

    private static void initKeyCurve(ECKey key) {
        key.setA(SECP256K1_A, (short)0, (short)SECP256K1_A.length);
        key.setB(SECP256K1_B, (short)0, (short)SECP256K1_B.length);
        key.setFieldFP(SECP256K1_FP, (short)0, (short)SECP256K1_FP.length);
        key.setG(SECP256K1_G, (short)0, (short)SECP256K1_G.length);
        key.setR(SECP256K1_R, (short)0, (short)SECP256K1_R.length);
        key.setK(SECP256K1_K);
    }

    public static void hashRipemd32(byte[] buffer, short offset, byte[] target, short targetOffset) {
        if (digestRipemd != null) {
            digestRipemd.doFinal(buffer, offset, (short)32, target, targetOffset);
        }
        else {
            Ripemd160.hash32(buffer, offset, target, targetOffset);
        }
    }

    public static void signTransientPrivate(byte[] keyBuffer, short keyOffset, byte[] dataBuffer, short dataOffset, byte[] targetBuffer, short targetOffset) {
        if (transientPrivateTransient) {
            initKeyCurve(transientPrivate);
        }
        transientPrivate.setS(keyBuffer, keyOffset, (short)32);
        Util.arrayFillNonAtomic(keyBuffer, keyOffset, (short)32, (byte)0x00);
        // recheck with the target platform, initializing once instead might be possible and save a few flash write
        // (this part is unspecified in the Java Card API)
        signature.init(transientPrivate, Signature.MODE_SIGN);
        signature.sign(dataBuffer, dataOffset, (short)32, targetBuffer, targetOffset);
        if (transientPrivateTransient) {
            transientPrivate.clearKey();
        }
    }

    private static KeyPair generatingPair;
    private static ECPrivateKey transientPrivate;
    private static boolean transientPrivateTransient;
    private static Signature signature;
    public static MessageDigest digestFull;
    public static MessageDigest digestAuthorization;
    public static MessageDigest digestScratch;
    private static MessageDigest digestRipemd;
    public static RandomData random;
    public static Cipher blobEncryptDecrypt;

    // Nice SECp256k1 constants, only available during NIST opening hours

    private static final byte SECP256K1_FP[] = {
        (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
        (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
        (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
        (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFE,(byte)0xFF,(byte)0xFF,(byte)0xFC,(byte)0x2F
    };
    private static final byte SECP256K1_A[] = {
        (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
        (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
        (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
        (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00
    };
    private static final byte SECP256K1_B[] = {
        (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
        (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
        (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
        (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x07
    };
    private static final byte SECP256K1_G[] = {
        (byte)0x04,
        (byte)0x79,(byte)0xBE,(byte)0x66,(byte)0x7E,(byte)0xF9,(byte)0xDC,(byte)0xBB,(byte)0xAC,
        (byte)0x55,(byte)0xA0,(byte)0x62,(byte)0x95,(byte)0xCE,(byte)0x87,(byte)0x0B,(byte)0x07,
        (byte)0x02,(byte)0x9B,(byte)0xFC,(byte)0xDB,(byte)0x2D,(byte)0xCE,(byte)0x28,(byte)0xD9,
        (byte)0x59,(byte)0xF2,(byte)0x81,(byte)0x5B,(byte)0x16,(byte)0xF8,(byte)0x17,(byte)0x98,
        (byte)0x48,(byte)0x3A,(byte)0xDA,(byte)0x77,(byte)0x26,(byte)0xA3,(byte)0xC4,(byte)0x65,
        (byte)0x5D,(byte)0xA4,(byte)0xFB,(byte)0xFC,(byte)0x0E,(byte)0x11,(byte)0x08,(byte)0xA8,
        (byte)0xFD,(byte)0x17,(byte)0xB4,(byte)0x48,(byte)0xA6,(byte)0x85,(byte)0x54,(byte)0x19,
        (byte)0x9C,(byte)0x47,(byte)0xD0,(byte)0x8F,(byte)0xFB,(byte)0x10,(byte)0xD4,(byte)0xB8
    };
    private static final byte SECP256K1_R[] = {
        (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
        (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFE,
        (byte)0xBA,(byte)0xAE,(byte)0xDC,(byte)0xE6,(byte)0xAF,(byte)0x48,(byte)0xA0,(byte)0x3B,
        (byte)0xBF,(byte)0xD2,(byte)0x5E,(byte)0x8C,(byte)0xD0,(byte)0x36,(byte)0x41,(byte)0x41
    };
    private static final byte SECP256K1_K = (byte)0x01;
}


text:
PASTADIR=$(dirname "$0")
python3 "$PASTADIR"/main.py $@


text:
struct LgcyWindow;
struct LgcyButton;
struct UiPromptPacket {
	int idx;
	void * unk4;
	const char* bodyText;
	TigRect textRect;
	const char* wndTitle;
	int image;
	int btnNormalTexture;
	int btn2NormalTexture;
	int btn3NormalTexture;
	int btnHoverTexture;
	int btn2HoverTexture;
	int btn3HoverTexture;
	int btnPressedTexture;
	int btn2PressedTexture;
	int btn3PressedTexture;
	int btnDisabledTexture;
	int btn2DisabledTexture;
	int btn3DisabledTexture;
	int(__cdecl* onPopupShow)();
	void(__cdecl* onPopupHide)();
	void(__cdecl* callback)(int popupBtnIdx);
	TigRect wndRect;
	TigRect okRect;
	const char* okBtnText;
	TigRect cancelRect;
	const char* cancelBtnText;
	TigRect textEntryRect;
	int unkA8;
	int unkAC;
	int unkB0;

	UiPromptPacket();
	int Show(int promptIdx, int flag);
	void Reset();
};

const int testSizeofPromptPacket = sizeof(UiPromptPacket); // should be 180 (0xB4)


text:
package genddl

import (
	"strings"
	"testing"
)

func TestJoinAndStripName(t *testing.T) {
	safeSize := []string{
		"aaaa",
		"bbbb",
		"cccc",
		"dddd",
		"eeee",
		"ffff",
		"gg",
	}
	ss1 := joinAndStripName(strings.Join(safeSize, "_"))
	if ss1 != strings.Join(safeSize, "_") {
		t.Errorf("return string is invalid: %s", ss1)
	}

	overSize := append(safeSize, "h")
	expectedIndexName := "aaaa_bbbb_cccc_dddd_eeeee98dee81"
	ss2 := joinAndStripName(strings.Join(overSize, "_"))
	if ss2 != expectedIndexName {
		t.Errorf("return string is invalid: %s", ss2)
	}
}


text:
/*
 * options.h
 *
 *  Created on: 28 lut 2014
 *      Author: Mateusz Stefaniak
 */

#ifndef OPTIONS_H_
#define OPTIONS_H_

#include <stdio.h>

/**
 * Structure, which holds options
 */
typedef struct {
		int options[12];
		/*
		 * 0  - generate PNG images
		 * 1  - generate SVG images
		 * 2  - generate heat map
		 * 3  - generate statistics
		 * 4  - print states to stdout
		 * 5  - silent (no basic information to stdout) mode
		 * 6  - torus simulation
		 * 7  - generate TXT files
		 * 8  - number of cycles
		 * 9  - frequency of dumps
		 * 10 - generate gnuplot files
		 * 11 - show help
		 */
		char strings[8][256];
		/*
		 * 0 - path_gen
		 * 1 - file_to_read
		 * 2 - file_structs_to_read
		 * 3 - rules
		 * 4 - path_history
		 * 5 - path_png
		 * 6 - path_svg
		 * 7 - path_txt
		 */
		FILE* gnuplot; /* file for gnuplot stats */
} options;

/**
 * Makes initialization of options structure.
 * @return			POINTER TO OPTIONS
 */
options*	options_init			();

/**
 * Parses options.
 * @param o			POINTER TO OPTIONS
 * @param argc_a	ARGS COUNT
 * @param argv_a	ARGC ARRAY
 * @return			1/0 - OK / ERRORS OCCURED
 */
int			options_parse		( options* o, int argc_a, char** argv_a);

/**
 * Sets default options.
 * @param o			POINTER TO OPTIONS
 */
void		options_set_defaults		( options* o );

/**
 * Checks if help argument is set.
 * @param argc          ARRAY LENGTH
 * @param argv          POINTER TO ARRAY
 * @return		0 IF NOT SET; 1 IF SET
 */
int             options_check_help		( int argc, char** argv );

/**
 * Checks for unsetted arguments in aguments array for thoose
 *  which are required to lauch program properly.
 * @param argc		ARRAY LENGTH
 * @param argv		POINTER TO ARRAY
 * @return
 */
int		options_check_min		( int argc, char** argv );

/**
 * Frees memory.
 * @param o			POINTER TO OPTIONS
 */
void		options_free			( options* o);

#endif /* OPTIONS_H_ */


text:
from jingo import register
from tower import ugettext_lazy as _lazy

from fjord.feedback.config import CODE_TO_COUNTRY

@register.filter
def country_name(country, native=False, default=_lazy(u'Unknown')):
    """Convert a country code into a human readable country name"""
    if country in CODE_TO_COUNTRY:
        display_locale = 'native' if native else 'English'
        return CODE_TO_COUNTRY[country][display_locale]
    else:
        return default


text:
"""Google Compute Engine resources."""

from typing import TYPE_CHECKING, Dict, Optional, Any

from libcloudforensics.providers.gcp.internal import common

if TYPE_CHECKING:
  import googleapiclient

class GoogleComputeBaseResource(common.GoogleCloudComputeClient):
  """Base class representing a Computer Engine resource.

  Attributes:
    project_id (str): Google Cloud project ID.
    zone (str): What zone the resource is in.
    name (str): Name of the resource.
    resource_id (str): The ID number of the resource.
    labels (Dict): Dictionary of labels for the resource, if existing.
    deletion_protection (bool): True if the resource has deletionProtection
        enabled.
  """

  def __init__(self,
               project_id: str,
               zone: str,
               name: str,
               resource_id: Optional[str] = None,
               labels: Optional[Dict[str, Any]] = None,
               deletion_protection: bool = False,
               region: Optional[str] = None) -> None:
    """Initialize the Google Compute Resource base object.

    Args:
      project_id: Google Cloud project ID.
      zone: What zone the resource is in.
      name: Name of the resource.
      resource_id: The ID number of the resource.
      labels: Dictionary of labels for the resource, if existing.
      deletion_protection: True if the resource has deletionProtection
          enabled.
      region: What region the resource is in.
    """

    self.deletion_protection = deletion_protection
    self.zone = zone
    self.name = name
    self.resource_id = resource_id
    self.labels = labels
    self._data = {}  # type: Dict[str, Any]
    self.project_id = project_id  # type: str
    self.region = region
    super().__init__(self.project_id)

  def FormatLogMessage(self, message: str) -> str:
    """Format log messages with project specific information.

    Args:
      message (str): Message string to log.

    Returns:
      str: Formatted log message string.
    """

    return 'project:{0} {1}'.format(self.project_id, message)

  def GetOperation(self) -> Dict[str, Any]:
    """Abstract method to be implemented by child classes.

    Raises:
      NotImplementedError: If the child class doesn't implement GetOperation.
    """
    raise NotImplementedError("GetOperation is not implemented by this class.")

  def GetValue(self, key: str) -> Any:
    """Get specific value from the resource key value store.

    Args:
      key (str): A key of type String to get key's corresponding value.

    Returns:
      str|Dict: Value of key/dictionary or None if key is missing.
    """

    self._data = self.GetOperation()
    return self._data.get(key)

  def GetSourceString(self) -> str:
    """API URL to the resource.

    Returns:
      str: The full API URL to the resource.
    """

    if self._data:
      url = self._data['selfLink']  # type: str
      return url
    url = self.GetValue('selfLink')
    return url

  def GetResourceType(self) -> str:
    """Get the resource type from the resource key-value store.

    Returns:
      str: Resource Type which is a string with one of the following values:
          compute#instance
          compute#disk
          compute#Snapshot
    """

    if self._data:
      resource_type = self._data['kind']  # type: str
      return resource_type
    resource_type = self.GetValue('kind')
    return resource_type

  def FormOperation(
      self,
      operation_name: str) -> 'googleapiclient.discovery.Resource':
    """Form an API operation object for the compute resource.

    Example:[RESOURCE].FormOperation('setLabels')(**kwargs)
    [RESOURCE] can be type "instance", disk or "Snapshot".

    Args:
      operation_name (str): The name of the API operation you need to perform.

    Returns:
      googleapiclient.discovery.Resource: An API operation object for the
          referenced compute resource.

    Raises:
      RuntimeError: If resource type is not defined as a type which
          extends the GoogleComputeBaseResource class.
    """

    resource_type = self.GetResourceType()
    module = None
    if resource_type not in ['compute#instance',
                             'compute#Snapshot',
                             'compute#disk']:
      error_msg = (
          'Compute resource Type {0:s} is not one of the defined '
          'types in libcloudforensics library '
          '(Instance, Disk or Snapshot).').format(resource_type)
      raise RuntimeError(error_msg)
    if resource_type == 'compute#instance':
      module = self.GceApi().instances() # pylint: disable=no-member
    elif resource_type == 'compute#disk':
      module = self.GceApi().disks() # pylint: disable=no-member
    elif resource_type == 'compute#Snapshot':
      module = self.GceApi().snapshots() # pylint: disable=no-member

    operation_func_to_call = getattr(module, operation_name)
    return operation_func_to_call

  def GetLabels(self) -> Dict[str, Any]:
    """Get all labels of a compute resource.

    Returns:
      Dict: A dictionary of all labels.
    """

    operation = self.GetOperation()  # type: Dict[str, Dict[str, Any]]
    labels = operation.get('labels')
    if labels:
      return labels
    return {}

  def AddLabels(self,
                new_labels_dict: Dict[str, Any],
                blocking_call: bool = False) -> Optional[Any]:
    """Add or update labels of a compute resource.

    Args:
      new_labels_dict (Dict): A dictionary containing the labels to be added,
          ex:{"incident_id": "1234abcd"}.
      blocking_call (bool): Optional. A boolean to decide whether the API call
          should be blocking or not, default is False.

    Returns:
      Optional[Any]: The response of the API operation (a Dict if the call is
          successful).

    Raises:
      RuntimeError: If the Compute resource Type is not one of instance,
          disk or snapshot.
    """

    get_operation = self.GetOperation()
    label_fingerprint = get_operation['labelFingerprint']

    existing_labels_dict = {}  # type: Dict[str, Any]
    if self.GetLabels() is not None:
      existing_labels_dict = self.GetLabels()
    existing_labels_dict.update(new_labels_dict)
    labels_dict = existing_labels_dict
    request_body = {
        'labels': labels_dict, 'labelFingerprint': label_fingerprint
    }

    resource_type = self.GetResourceType()
    response = {}
    if resource_type not in ['compute#instance',
                             'compute#Snapshot',
                             'compute#disk']:
      error_msg = (
          'Compute resource Type {0:s} is not one of the defined '
          'types in libcloudforensics library '
          '(Instance, Disk or Snapshot) ').format(resource_type)
      raise RuntimeError(error_msg)
    if resource_type == 'compute#instance':
      response = self.FormOperation('setLabels')(
          instance=self.name,
          project=self.project_id,
          zone=self.zone,
          body=request_body).execute()
    elif resource_type == 'compute#disk':
      response = self.FormOperation('setLabels')(
          resource=self.name,
          project=self.project_id,
          zone=self.zone,
          body=request_body).execute()
    elif resource_type == 'compute#Snapshot':
      response = self.FormOperation('setLabels')(
          resource=self.name, project=self.project_id,
          body=request_body).execute()
    if blocking_call:
      self.BlockOperation(response, zone=self.zone)

    return response


text:
//===--- ImmutableTextBuffer.cpp ------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "SourceKit/Support/ImmutableTextBuffer.h"
#include "clang/Rewrite/Core/RewriteRope.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"

using namespace SourceKit;
using namespace llvm;
using clang::RewriteRope;

void ImmutableTextUpdate::anchor() {}

ImmutableTextBuffer::ImmutableTextBuffer(
    std::unique_ptr<llvm::MemoryBuffer> MemBuf, uint64_t Stamp)
  : ImmutableTextUpdate(Kind::Buffer, Stamp) {
    SrcMgr.reset(new SourceMgr);
    BufId = SrcMgr->AddNewSourceBuffer(std::move(MemBuf), SMLoc());
  }

ImmutableTextBuffer::ImmutableTextBuffer(StringRef Filename, StringRef Text,
                                         uint64_t Stamp)
  : ImmutableTextBuffer(
      std::unique_ptr<llvm::MemoryBuffer>(
        MemoryBuffer::getMemBufferCopy(Text, Filename)),
      Stamp) {
}

StringRef ImmutableTextBuffer::getFilename() const {
  return SrcMgr->getMemoryBuffer(BufId)->getBufferIdentifier();
}

StringRef ImmutableTextBuffer::getText() const {

  return SrcMgr->getMemoryBuffer(BufId)->getBuffer();
}

const llvm::MemoryBuffer *ImmutableTextBuffer::getInternalBuffer() const {
  return SrcMgr->getMemoryBuffer(BufId);
}

std::pair<unsigned, unsigned>
ImmutableTextBuffer::getLineAndColumn(unsigned ByteOffset) const {
  auto Buf = SrcMgr->getMemoryBuffer(BufId);
  if (ByteOffset > Buf->getBufferSize())
    return std::make_pair(0, 0);

  SMLoc Loc = SMLoc::getFromPointer(Buf->getBufferStart() + ByteOffset);
  return SrcMgr->getLineAndColumn(Loc, 0);
}

ReplaceImmutableTextUpdate::ReplaceImmutableTextUpdate(
    unsigned ByteOffset, unsigned Length,
    StringRef Text, uint64_t Stamp)
  : ImmutableTextUpdate(Kind::Replace, Stamp),
    Buf(llvm::MemoryBuffer::getMemBufferCopy(Text)),
    ByteOffset(ByteOffset), Length(Length) {
}

StringRef ReplaceImmutableTextUpdate::getText() const {
  return Buf->getBuffer();
}

StringRef ImmutableTextSnapshot::getFilename() const {
  return EditableBuf->getFilename();
}

uint64_t ImmutableTextSnapshot::getStamp() const {
  return DiffEnd->getStamp();
}

ImmutableTextBufferRef ImmutableTextSnapshot::getBuffer() const {
  return EditableBuf->getBufferForSnapshot(*this);
}

bool ImmutableTextSnapshot::precedesOrSame(ImmutableTextSnapshotRef Other) {
  assert(Other);

  ImmutableTextUpdateRef Upd = this->DiffEnd;
  while (Upd) {
    if (Upd == Other->DiffEnd) {
      return true;
    }
    Upd = Upd->getNext();
  }

  return false;
}

bool ImmutableTextSnapshot::foreachReplaceUntil(
    ImmutableTextSnapshotRef EndSnapshot,
    std::function<bool(ReplaceImmutableTextUpdateRef Upd)> Fn) {

  assert(EndSnapshot);
  ImmutableTextUpdateRef Upd = DiffEnd;
  while (Upd != EndSnapshot->DiffEnd) {
    Upd = Upd->getNext();
    if (!Upd) {
      assert(0 && "Did not find end snapshot");
      break;
    }
    if (auto ReplaceUpd = dyn_cast<ReplaceImmutableTextUpdate>(Upd))
      if (!Fn(ReplaceUpd))
        return false;
  }

  return true;
}

static std::atomic<uint64_t> Generation{ 0 };

EditableTextBuffer::EditableTextBuffer(StringRef Filename, StringRef Text) {
  this->Filename = Filename;
  Root = new ImmutableTextBuffer(Filename, Text, ++Generation);
  CurrUpd = Root;
}

ImmutableTextSnapshotRef EditableTextBuffer::getSnapshot() const {
  llvm::sys::ScopedLock L(EditMtx);
  return new ImmutableTextSnapshot(const_cast<EditableTextBuffer*>(this), Root,
                                   CurrUpd);
}

ImmutableTextSnapshotRef EditableTextBuffer::insert(unsigned ByteOffset,
    StringRef Text) {
  ImmutableTextUpdateRef NewUpd =
      new ReplaceImmutableTextUpdate(ByteOffset, /*Length=*/0, Text,
                                     ++Generation);
  return addAtomicUpdate(std::move(NewUpd));
}

ImmutableTextSnapshotRef EditableTextBuffer::erase(unsigned ByteOffset,
                                                   unsigned Length) {
  ImmutableTextUpdateRef NewUpd =
      new ReplaceImmutableTextUpdate(ByteOffset, Length, StringRef(),
                                     ++Generation);
  return addAtomicUpdate(std::move(NewUpd));
}

ImmutableTextSnapshotRef EditableTextBuffer::replace(unsigned ByteOffset,
                                                     unsigned Length,
                                                     StringRef Text) {
  ImmutableTextUpdateRef NewUpd =
      new ReplaceImmutableTextUpdate(ByteOffset, Length, Text, ++Generation);
  return addAtomicUpdate(std::move(NewUpd));
}

ImmutableTextSnapshotRef EditableTextBuffer::addAtomicUpdate(
    ImmutableTextUpdateRef NewUpd) {

  llvm::sys::ScopedLock L(EditMtx);

  refresh();

  assert(CurrUpd->Next == nullptr);
  CurrUpd->Next = NewUpd;
  CurrUpd = NewUpd;

  return new ImmutableTextSnapshot(this, Root, CurrUpd);
}

static std::unique_ptr<llvm::MemoryBuffer>
getMemBufferFromRope(StringRef Filename, const RewriteRope &Rope) {
  size_t Length = 0;
  for (RewriteRope::iterator I = Rope.begin(), E = Rope.end(); I != E;
       I.MoveToNextPiece()) {
    Length += I.piece().size();
  }

  auto MemBuf = llvm::MemoryBuffer::getNewUninitMemBuffer(Length, Filename);
  char *Ptr = (char*)MemBuf->getBufferStart();
  for (RewriteRope::iterator I = Rope.begin(), E = Rope.end(); I != E;
       I.MoveToNextPiece()) {
    StringRef Text = I.piece();
    memcpy(Ptr, Text.data(), Text.size());
    Ptr += Text.size();
  }

  return MemBuf;
}

ImmutableTextBufferRef EditableTextBuffer::getBufferForSnapshot(
    const ImmutableTextSnapshot &Snap) {
  if (auto Buf = dyn_cast<ImmutableTextBuffer>(Snap.DiffEnd))
    return Buf;
  ImmutableTextUpdateRef Next = Snap.DiffEnd->Next;
  // FIXME: dyn_cast_null does not work with IntrusiveRefCntPtr.
  if (Next)
    if (auto Buf = dyn_cast<ImmutableTextBuffer>(Next))
      return Buf;

  // Check if a buffer was created in the middle of the snapshot updates.
  ImmutableTextBufferRef StartBuf = Snap.BufferStart;
  ImmutableTextUpdateRef Upd = StartBuf;
  while (Upd != Snap.DiffEnd) {
    Upd = Upd->Next;
    if (auto Buf = dyn_cast<ImmutableTextBuffer>(Upd))
      StartBuf = Buf;
  }
  StringRef StartText = StartBuf->getText();

  RewriteRope Rope;
  auto applyUpdate = [&](const ImmutableTextUpdateRef &Upd) {
    if (auto ReplaceUpd = dyn_cast<ReplaceImmutableTextUpdate>(Upd)) {
      Rope.erase(ReplaceUpd->getByteOffset(), ReplaceUpd->getLength());
      StringRef Text = ReplaceUpd->getText();
      Rope.insert(ReplaceUpd->getByteOffset(), Text.begin(), Text.end());
    }
  };

  Rope.assign(StartText.begin(), StartText.end());
  Upd = StartBuf;
  while (Upd != Snap.DiffEnd) {
    Upd = Upd->Next;
    applyUpdate(Upd);
  }

  auto MemBuf = getMemBufferFromRope(getFilename(), Rope);
  ImmutableTextBufferRef ImmBuf = new ImmutableTextBuffer(std::move(MemBuf),
                                                          Snap.getStamp());

  {
    llvm::sys::ScopedLock L(EditMtx);
    ImmBuf->Next = Snap.DiffEnd->Next;
    Snap.DiffEnd->Next = ImmBuf;
    refresh();
  }
  return ImmBuf;
}

// This should always be called under the mutex lock.
void EditableTextBuffer::refresh() {
  while (CurrUpd->Next) {
    CurrUpd = CurrUpd->Next;
    if (auto Buf = dyn_cast<ImmutableTextBuffer>(CurrUpd))
      Root = Buf;
  }
}

EditableTextBufferRef EditableTextBufferManager::getOrCreateBuffer(
    StringRef Filename,
    StringRef Text) {

  llvm::sys::ScopedLock L(Mtx);

  assert(!Filename.empty());
  EditableTextBufferRef &EdBuf = FileBufferMap[Filename];
  if (!EdBuf)
    EdBuf = new EditableTextBuffer(Filename, Text);

  return EdBuf;
}

EditableTextBufferRef
EditableTextBufferManager::resetBuffer(StringRef Filename, StringRef Text) {
  llvm::sys::ScopedLock L(Mtx);

  assert(!Filename.empty());
  EditableTextBufferRef &EdBuf = FileBufferMap[Filename];
  EdBuf = new EditableTextBuffer(Filename, Text);
  return EdBuf;
}