1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "logical-to-physical-map.h"
6
7#include <fbl/algorithm.h>
8
9namespace nand {
10
11LogicalToPhysicalMap::LogicalToPhysicalMap(uint32_t copies, uint32_t block_count,
12                                           fbl::Array<uint32_t> bad_blocks)
13    : copies_(copies), block_count_(block_count), bad_blocks_(fbl::move(bad_blocks)) {
14    ZX_ASSERT(block_count_ > 0);
15    ZX_ASSERT(block_count_ >= bad_blocks_.size());
16    ZX_ASSERT(block_count_ % copies_ == 0);
17
18    qsort(bad_blocks_.get(), bad_blocks_.size(), sizeof(uint32_t),
19          [](const void* l, const void* r) {
20              const auto* left = static_cast<const uint32_t*>(l);
21              const auto* right = static_cast<const uint32_t*>(r);
22              if (*left < *right) {
23                  return -1;
24              } else if (*left > *right) {
25                  return 1;
26              }
27              return 0;
28          });
29}
30
31zx_status_t LogicalToPhysicalMap::GetPhysical(uint32_t copy, uint32_t block,
32                                              uint32_t* physical_block) const {
33    ZX_ASSERT(copy < copies_);
34
35    const uint32_t blocks_per_copy = block_count_ / copies_;
36    const uint32_t first = copy * blocks_per_copy;
37    const uint32_t last = first + blocks_per_copy - 1;
38    block += first;
39    uint32_t skipped_blocks = 0;
40    for (const auto& bad_block : bad_blocks_) {
41        if (bad_block != fbl::clamp(bad_block, first, last)) {
42            continue;
43        }
44
45        if (block + skipped_blocks < bad_block) {
46            *physical_block = block + skipped_blocks;
47            return ZX_OK;
48        }
49        skipped_blocks++;
50    }
51    if (block + skipped_blocks <= last) {
52        *physical_block = block + skipped_blocks;
53        return ZX_OK;
54    }
55
56    return ZX_ERR_OUT_OF_RANGE;
57}
58
59uint32_t LogicalToPhysicalMap::LogicalBlockCount(uint32_t copy) const {
60    ZX_ASSERT(copy < copies_);
61    const uint32_t blocks_per_copy = block_count_ / copies_;
62    const uint32_t first = copy * blocks_per_copy;
63    const uint32_t last = first + blocks_per_copy - 1;
64
65    uint32_t bad_block_count = 0;
66    for (const auto& bad_block : bad_blocks_) {
67        if (bad_block == fbl::clamp(bad_block, first, last)) {
68            bad_block_count++;
69        }
70    }
71    return blocks_per_copy - bad_block_count;
72}
73
74} // namespace nand
75