| 160 | } |
| 161 | |
| 162 | Status RamFileBlockCache::Read(const string& filename, size_t offset, size_t n, |
| 163 | char* buffer, size_t* bytes_transferred) { |
| 164 | *bytes_transferred = 0; |
| 165 | if (n == 0) { |
| 166 | return Status::OK(); |
| 167 | } |
| 168 | if (!IsCacheEnabled() || (n > max_bytes_)) { |
| 169 | // The cache is effectively disabled, so we pass the read through to the |
| 170 | // fetcher without breaking it up into blocks. |
| 171 | return block_fetcher_(filename, offset, n, buffer, bytes_transferred); |
| 172 | } |
| 173 | // Calculate the block-aligned start and end of the read. |
| 174 | size_t start = block_size_ * (offset / block_size_); |
| 175 | size_t finish = block_size_ * ((offset + n) / block_size_); |
| 176 | if (finish < offset + n) { |
| 177 | finish += block_size_; |
| 178 | } |
| 179 | size_t total_bytes_transferred = 0; |
| 180 | // Now iterate through the blocks, reading them one at a time. |
| 181 | for (size_t pos = start; pos < finish; pos += block_size_) { |
| 182 | Key key = std::make_pair(filename, pos); |
| 183 | // Look up the block, fetching and inserting it if necessary, and update the |
| 184 | // LRU iterator for the key and block. |
| 185 | std::shared_ptr<Block> block = Lookup(key); |
| 186 | DCHECK(block) << "No block for key " << key.first << "@" << key.second; |
| 187 | TF_RETURN_IF_ERROR(MaybeFetch(key, block)); |
| 188 | TF_RETURN_IF_ERROR(UpdateLRU(key, block)); |
| 189 | // Copy the relevant portion of the block into the result buffer. |
| 190 | const auto& data = block->data; |
| 191 | if (offset >= pos + data.size()) { |
| 192 | // The requested offset is at or beyond the end of the file. This can |
| 193 | // happen if `offset` is not block-aligned, and the read returns the last |
| 194 | // block in the file, which does not extend all the way out to `offset`. |
| 195 | *bytes_transferred = total_bytes_transferred; |
| 196 | return errors::OutOfRange("EOF at offset ", offset, " in file ", filename, |
| 197 | " at position ", pos, "with data size ", |
| 198 | data.size()); |
| 199 | } |
| 200 | auto begin = data.begin(); |
| 201 | if (offset > pos) { |
| 202 | // The block begins before the slice we're reading. |
| 203 | begin += offset - pos; |
| 204 | } |
| 205 | auto end = data.end(); |
| 206 | if (pos + data.size() > offset + n) { |
| 207 | // The block extends past the end of the slice we're reading. |
| 208 | end -= (pos + data.size()) - (offset + n); |
| 209 | } |
| 210 | if (begin < end) { |
| 211 | size_t bytes_to_copy = end - begin; |
| 212 | memcpy(&buffer[total_bytes_transferred], &*begin, bytes_to_copy); |
| 213 | total_bytes_transferred += bytes_to_copy; |
| 214 | } |
| 215 | if (data.size() < block_size_) { |
| 216 | // The block was a partial block and thus signals EOF at its upper bound. |
| 217 | break; |
| 218 | } |
| 219 | } |