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 
9 #include <utility>
10 
11 namespace nand {
12 
LogicalToPhysicalMap(uint32_t copies,uint32_t block_count,fbl::Array<uint32_t> bad_blocks)13 LogicalToPhysicalMap::LogicalToPhysicalMap(uint32_t copies, uint32_t block_count,
14                                            fbl::Array<uint32_t> bad_blocks)
15     : copies_(copies), block_count_(block_count), bad_blocks_(std::move(bad_blocks)) {
16     ZX_ASSERT(block_count_ > 0);
17     ZX_ASSERT(block_count_ >= bad_blocks_.size());
18     ZX_ASSERT(block_count_ % copies_ == 0);
19 
20     qsort(bad_blocks_.get(), bad_blocks_.size(), sizeof(uint32_t),
21           [](const void* l, const void* r) {
22               const auto* left = static_cast<const uint32_t*>(l);
23               const auto* right = static_cast<const uint32_t*>(r);
24               if (*left < *right) {
25                   return -1;
26               } else if (*left > *right) {
27                   return 1;
28               }
29               return 0;
30           });
31 }
32 
GetPhysical(uint32_t copy,uint32_t block,uint32_t * physical_block) const33 zx_status_t LogicalToPhysicalMap::GetPhysical(uint32_t copy, uint32_t block,
34                                               uint32_t* physical_block) const {
35     ZX_ASSERT(copy < copies_);
36 
37     const uint32_t blocks_per_copy = block_count_ / copies_;
38     const uint32_t first = copy * blocks_per_copy;
39     const uint32_t last = first + blocks_per_copy - 1;
40     block += first;
41     uint32_t skipped_blocks = 0;
42     for (const auto& bad_block : bad_blocks_) {
43         if (bad_block != fbl::clamp(bad_block, first, last)) {
44             continue;
45         }
46 
47         if (block + skipped_blocks < bad_block) {
48             *physical_block = block + skipped_blocks;
49             return ZX_OK;
50         }
51         skipped_blocks++;
52     }
53     if (block + skipped_blocks <= last) {
54         *physical_block = block + skipped_blocks;
55         return ZX_OK;
56     }
57 
58     return ZX_ERR_OUT_OF_RANGE;
59 }
60 
LogicalBlockCount(uint32_t copy) const61 uint32_t LogicalToPhysicalMap::LogicalBlockCount(uint32_t copy) const {
62     ZX_ASSERT(copy < copies_);
63     const uint32_t blocks_per_copy = block_count_ / copies_;
64     const uint32_t first = copy * blocks_per_copy;
65     const uint32_t last = first + blocks_per_copy - 1;
66 
67     uint32_t bad_block_count = 0;
68     for (const auto& bad_block : bad_blocks_) {
69         if (bad_block == fbl::clamp(bad_block, first, last)) {
70             bad_block_count++;
71         }
72     }
73     return blocks_per_copy - bad_block_count;
74 }
75 
76 } // namespace nand
77