17e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
27e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh// Use of this source code is governed by a BSD-style license that can be
37e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh// found in the LICENSE file.
47e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
57e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh#include "address_mapper.h"
67e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
77e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh#include "base/logging.h"
87e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
97e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshnamespace quipper {
107e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
117e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshAddressMapper::AddressMapper(const AddressMapper& source) {
127e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  mappings_ = source.mappings_;
137e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}
147e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
157e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshbool AddressMapper::Map(const uint64_t real_addr,
167e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                        const uint64_t size,
177e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                        const bool remove_existing_mappings) {
187e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  return MapWithID(real_addr, size, kuint64max, 0, remove_existing_mappings);
197e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}
207e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
217e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshbool AddressMapper::MapWithID(const uint64_t real_addr,
227e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                              const uint64_t size,
237e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                              const uint64_t id,
247e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                              const uint64_t offset_base,
257e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                              bool remove_existing_mappings) {
267e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappedRange range;
277e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  range.real_addr = real_addr;
287e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  range.size = size;
297e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  range.id = id;
307e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  range.offset_base = offset_base;
317e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
327e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  if (size == 0) {
337e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    LOG(ERROR) << "Must allocate a nonzero-length address range.";
347e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return false;
357e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
367e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
377e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // Check that this mapping does not overflow the address space.
387e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  if (real_addr + size - 1 != kuint64max &&
397e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      !(real_addr + size > real_addr)) {
407e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    DumpToLog();
417e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    LOG(ERROR) << "Address mapping at " << std::hex << real_addr
427e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh               << " with size " << std::hex << size << " overflows.";
437e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return false;
447e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
457e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
467e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // Check for collision with an existing mapping.  This must be an overlap that
477e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // does not result in one range being completely covered by another
487e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappingList::iterator iter;
497e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappingList mappings_to_delete;
507e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  bool old_range_found = false;
517e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappedRange old_range;
527e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
537e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (!iter->Intersects(range))
547e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      continue;
557e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    // Quit if existing ranges that collide aren't supposed to be removed.
567e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (!remove_existing_mappings)
577e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      return false;
587e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (!old_range_found && iter->Covers(range) && iter->size > range.size) {
597e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      old_range_found = true;
607e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      old_range = *iter;
617e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      continue;
627e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    }
637e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    mappings_to_delete.push_back(*iter);
647e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
657e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
667e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  while (!mappings_to_delete.empty()) {
677e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    const MappedRange& range = mappings_to_delete.front();
687e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    CHECK(Unmap(range));
697e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    mappings_to_delete.pop_front();
707e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
717e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
727e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // Otherwise check for this range being covered by another range.  If that
737e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // happens, split or reduce the existing range to make room.
747e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  if (old_range_found) {
757e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    CHECK(Unmap(old_range));
767e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
777e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    uint64_t gap_before = range.real_addr - old_range.real_addr;
787e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    uint64_t gap_after = (old_range.real_addr + old_range.size) -
797e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                         (range.real_addr + range.size);
807e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
817e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (gap_before) {
827e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      CHECK(MapWithID(old_range.real_addr,
837e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                      gap_before,
847e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                      old_range.id,
857e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                      old_range.offset_base,
867e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                      false));
877e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    }
887e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
897e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    CHECK(MapWithID(range.real_addr, range.size, id, offset_base, false));
907e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
917e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (gap_after) {
927e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      CHECK(MapWithID(range.real_addr + range.size,
937e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                      gap_after,
947e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                      old_range.id,
957e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                      old_range.offset_base + gap_before + range.size,
967e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                      false));
977e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    }
987e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
997e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return true;
1007e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
1017e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1027e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // Now search for a location for the new range.  It should be in the first
1037e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // free block in quipper space.
1047e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1057e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // If there is no existing mapping, add it to the beginning of quipper space.
1067e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  if (mappings_.empty()) {
1077e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    range.mapped_addr = 0;
1087e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    range.unmapped_space_after = kuint64max - range.size;
1097e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    mappings_.push_back(range);
1107e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return true;
1117e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
1127e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1137e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // If there is space before the first mapped range in quipper space, use it.
1147e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  if (mappings_.begin()->mapped_addr >= range.size) {
1157e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    range.mapped_addr = 0;
1167e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    range.unmapped_space_after = mappings_.begin()->mapped_addr - range.size;
1177e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    mappings_.push_front(range);
1187e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return true;
1197e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
1207e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1217e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // Otherwise, search through the existing mappings for a free block after one
1227e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // of them.
1237e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
1247e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (iter->unmapped_space_after < range.size)
1257e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      continue;
1267e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1277e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    range.mapped_addr = iter->mapped_addr + iter->size;
1287e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    range.unmapped_space_after = iter->unmapped_space_after - range.size;
1297e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    iter->unmapped_space_after = 0;
1307e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1317e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    mappings_.insert(++iter, range);
1327e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return true;
1337e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
1347e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1357e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // If it still hasn't succeeded in mapping, it means there is no free space in
1367e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // quipper space large enough for a mapping of this size.
1377e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  DumpToLog();
1387e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  LOG(ERROR) << "Could not find space to map addr=" << std::hex << real_addr
1397e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh             << " with size " << std::hex << size;
1407e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  return false;
1417e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}
1427e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1437e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshvoid AddressMapper::DumpToLog() const {
1447e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappingList::const_iterator it;
1457e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  for (it = mappings_.begin(); it != mappings_.end(); ++it) {
1467e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    LOG(INFO) << " real_addr: " << std::hex << it->real_addr
1477e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh              << " mapped: " << std::hex << it->mapped_addr
1487e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh              << " id: " << std::hex << it->id
1497e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh              << " size: " << std::hex << it->size;
1507e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
1517e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}
1527e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1537e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshbool AddressMapper::GetMappedAddress(const uint64_t real_addr,
1547e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                                     uint64_t* mapped_addr) const {
1557e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  CHECK(mapped_addr);
1567e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappingList::const_iterator iter;
1577e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
1587e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (!iter->ContainsAddress(real_addr))
1597e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      continue;
1607e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    *mapped_addr = iter->mapped_addr + real_addr - iter->real_addr;
1617e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return true;
1627e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
1637e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  return false;
1647e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}
1657e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1667e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshbool AddressMapper::GetMappedIDAndOffset(const uint64_t real_addr,
1677e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                                         uint64_t* id,
1687e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh                                         uint64_t* offset) const {
1697e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  CHECK(id);
1707e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  CHECK(offset);
1717e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappingList::const_iterator iter;
1727e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
1737e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (!iter->ContainsAddress(real_addr))
1747e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      continue;
1757e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    *id = iter->id;
1767e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    *offset = real_addr - iter->real_addr + iter->offset_base;
1777e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return true;
1787e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
1797e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  return false;
1807e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}
1817e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1827e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshuint64_t AddressMapper::GetMaxMappedLength() const {
1837e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  if (IsEmpty())
1847e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    return 0;
1857e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1867e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  uint64_t min = mappings_.begin()->mapped_addr;
1877e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1887e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappingList::const_iterator iter = mappings_.end();
1897e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  --iter;
1907e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  uint64_t max = iter->mapped_addr + iter->size;
1917e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1927e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  return max - min;
1937e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}
1947e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
1957e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntoshbool AddressMapper::Unmap(const MappedRange& range) {
1967e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  MappingList::iterator iter;
1977e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // TODO(sque): this is highly inefficient since Unmap() is called from a
1987e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // function that has already iterated to the right place within |mappings_|.
1997e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // For a first revision, I am sacrificing efficiency for of clarity, due to
2007e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  // the trickiness of removing elements using iterators.
2017e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
2027e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    if (range.real_addr == iter->real_addr && range.size == iter->size) {
2037e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      // Add the freed up space to the free space counter of the previous
2047e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      // mapped region, if it exists.
2057e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      if (iter != mappings_.begin()) {
2067e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh        --iter;
2077e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh        iter->unmapped_space_after += range.size + range.unmapped_space_after;
2087e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh        ++iter;
2097e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      }
2107e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      mappings_.erase(iter);
2117e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh      return true;
2127e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh    }
2137e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  }
2147e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh  return false;
2157e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}
2167e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh
2177e2f4e9d384d501cf86118ebac4b8de2b86eac53Than McIntosh}  // namespace quipper
218