F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
Chunk.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title Chunk.cpp
3 // \brief CFDP chunks (sparse gap tracking) logic file
4 //
5 // This file is a port of the cf_chunks.c file from the
6 // NASA Core Flight System (cFS) CFDP (CF) Application,
7 // version 3.0.0, adapted for use within the F-Prime (F') framework.
8 //
9 // This class handles the complexity of sparse gap tracking so that
10 // the CFDP engine doesn't need to worry about it. Information is given
11 // to the class and when needed calculations are made internally to
12 // help the engine build NAK packets. Received NAK segment requests
13 // are stored in this class as well and used for re-transmit processing.
14 //
15 // ======================================================================
16 //
17 // NASA Docket No. GSC-18,447-1
18 //
19 // Copyright (c) 2019 United States Government as represented by the
20 // Administrator of the National Aeronautics and Space Administration.
21 // All Rights Reserved.
22 //
23 // Licensed under the Apache License, Version 2.0 (the "License"); you may
24 // not use this file except in compliance with the License. You may obtain
25 // a copy of the License at
26 //
27 // http://www.apache.org/licenses/LICENSE-2.0
28 //
29 // Unless required by applicable law or agreed to in writing, software
30 // distributed under the License is distributed on an "AS IS" BASIS,
31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32 // See the License for the specific language governing permissions and
33 // limitations under the License.
34 //
35 // ======================================================================
36 
37 #include <string.h>
38 
39 #include <Fw/Types/Assert.hpp>
40 
42 
43 namespace Svc {
44 namespace Ccsds {
45 namespace Cfdp {
46 
47 // ======================================================================
48 // CfdpChunkList Class Implementation
49 // ======================================================================
50 
52  : m_count(0), m_maxChunks(maxChunks), m_chunks(chunkMem) {
53  FW_ASSERT(maxChunks > 0);
54  FW_ASSERT(chunkMem != nullptr);
55  reset();
56 }
57 
59  m_count = 0;
60  memset(m_chunks, 0, sizeof(*m_chunks) * m_maxChunks);
61 }
62 
63 void CfdpChunkList::add(FileSize offset, FileSize size) {
64  const Chunk chunk = {offset, size};
65  const ChunkIdx i = findInsertPosition(&chunk);
66 
67  // PTFO: files won't be so big we need to gracefully handle overflow,
68  // and in that case the user should change everything in chunks
69  // to use 64-bit numbers
70  FW_ASSERT((offset + size) >= offset, static_cast<FwAssertArgType>(offset), static_cast<FwAssertArgType>(size));
71 
72  insert(i, &chunk);
73 }
74 
76  return m_count ? &m_chunks[0] : nullptr;
77 }
78 
80  Chunk* chunk = &m_chunks[0]; /* front is always 0 */
81 
82  if (size > chunk->size) {
83  size = chunk->size;
84  }
85  chunk->size -= size;
86 
87  if (!chunk->size) {
88  eraseChunk(0);
89  } else {
90  chunk->offset += size;
91  }
92 }
93 
95  FileSize total,
96  FileSize start,
97  GapComputeCallback callback,
98  void* opaque) const {
99  U32 ret = 0;
100  ChunkIdx i = 0;
101  FileSize next_off;
102  FileSize gap_start;
103  Chunk chunk;
104 
105  FW_ASSERT(total); /* does it make sense to have a 0 byte file? */
106  FW_ASSERT(start < total, static_cast<FwAssertArgType>(start), static_cast<FwAssertArgType>(total));
107 
108  /* simple case: there is no chunk data, which means there is a single gap of the entire size */
109  if (!m_count) {
110  chunk.offset = 0;
111  chunk.size = total;
112  if (callback) {
113  callback(&chunk, opaque);
114  }
115  ret = 1;
116  } else {
117  /* Handle initial gap if needed */
118  if (start < m_chunks[0].offset) {
119  chunk.offset = start;
120  chunk.size = m_chunks[0].offset - start;
121  if (callback) {
122  callback(&chunk, opaque);
123  }
124  ret = 1;
125  }
126 
127  while ((ret < maxGaps) && (i < m_count)) {
128  next_off = (i == (m_count - 1)) ? total : m_chunks[i + 1].offset;
129  gap_start = (m_chunks[i].offset + m_chunks[i].size);
130 
131  chunk.offset = (gap_start > start) ? gap_start : start;
132  chunk.size = (next_off - chunk.offset);
133 
134  if (gap_start >= total) {
135  break;
136  } else if (start < next_off) {
137  /* Only report if gap finishes after start */
138  if (callback) {
139  callback(&chunk, opaque);
140  }
141  ++ret;
142  }
143  ++i;
144  }
145  }
146 
147  return ret;
148 }
149 
150 void CfdpChunkList::insertChunk(ChunkIdx index, const Chunk* chunk) {
151  FW_ASSERT(m_count < m_maxChunks, m_count, m_maxChunks);
152  FW_ASSERT(index <= m_count, index, m_count);
153 
154  if (m_count && (index != m_count)) {
155  memmove(&m_chunks[index + 1], &m_chunks[index], sizeof(*chunk) * (m_count - index));
156  }
157  memcpy(&m_chunks[index], chunk, sizeof(*chunk));
158 
159  ++m_count;
160 }
161 
162 void CfdpChunkList::eraseChunk(ChunkIdx index) {
163  FW_ASSERT(m_count > 0);
164  FW_ASSERT(index < m_count, index, m_count);
165 
166  /* to erase, move memory over the old one */
167  memmove(&m_chunks[index], &m_chunks[index + 1], sizeof(*m_chunks) * (m_count - 1 - index));
168  --m_count;
169 }
170 
171 void CfdpChunkList::eraseRange(ChunkIdx start, ChunkIdx end) {
172  /* Sanity check */
173  FW_ASSERT(end <= m_count, end, m_count);
174 
175  if (start < end) {
176  memmove(&m_chunks[start], &m_chunks[end], sizeof(*m_chunks) * (m_count - end));
177  m_count = static_cast<ChunkIdx>(m_count - static_cast<ChunkIdx>(end - start));
178  }
179 }
180 
181 ChunkIdx CfdpChunkList::findInsertPosition(const Chunk* chunk) {
182  ChunkIdx first = 0;
183  ChunkIdx i;
184  ChunkIdx count = m_count;
185  ChunkIdx step;
186 
187  while (count > 0) {
188  i = first;
189  step = static_cast<ChunkIdx>(count / 2);
190  i = static_cast<ChunkIdx>(i + step);
191  if (m_chunks[i].offset < chunk->offset) {
192  first = static_cast<ChunkIdx>(i + 1);
193  count = static_cast<ChunkIdx>(count - static_cast<ChunkIdx>(step + 1));
194  } else {
195  count = step;
196  }
197  }
198 
199  return first;
200 }
201 
202 bool CfdpChunkList::combineNext(ChunkIdx i, const Chunk* chunk) {
203  ChunkIdx combined_i = i;
204  bool ret = false;
205  FileSize chunk_end = chunk->offset + chunk->size;
206 
207  /* Assert no rollover, only possible as a bug */
208  FW_ASSERT(chunk_end > chunk->offset, static_cast<FwAssertArgType>(chunk_end),
209  static_cast<FwAssertArgType>(chunk->offset));
210 
211  /* Determine how many can be combined */
212  for (; combined_i < m_count; ++combined_i) {
213  /* Advance combine index until there is a gap between end and the next offset */
214  if (chunk_end < m_chunks[combined_i].offset) {
215  break;
216  }
217  }
218 
219  /* If index advanced the range of chunks can be combined */
220  if (i != combined_i) {
221  /* End is the max of last combined chunk end or new chunk end */
222  chunk_end = CfdpChunkMax(m_chunks[combined_i - 1].offset + m_chunks[combined_i - 1].size, chunk_end);
223 
224  /* Use current slot as combined entry */
225  m_chunks[i].size = chunk_end - chunk->offset;
226  m_chunks[i].offset = chunk->offset;
227 
228  /* Erase the rest of the combined chunks (if any) */
229  eraseRange(static_cast<ChunkIdx>(i + 1), combined_i);
230  ret = true;
231  }
232 
233  return ret;
234 }
235 
236 bool CfdpChunkList::combinePrevious(ChunkIdx i, const Chunk* chunk) {
237  Chunk* prev;
238  FileSize prev_end;
239  FileSize chunk_end;
240  bool ret = false;
241 
242  FW_ASSERT(i <= m_maxChunks, i, m_maxChunks);
243 
244  /* Only need to check if there is a previous */
245  if (i > 0) {
246  chunk_end = chunk->offset + chunk->size;
247  prev = &m_chunks[i - 1];
248  prev_end = prev->offset + prev->size;
249 
250  /* Check if start of new chunk is less than end of previous (overlaps) */
251  if (chunk->offset <= prev_end) {
252  /* When combining, use the bigger of the two endings */
253  if (prev_end < chunk_end) {
254  /* Combine with previous chunk */
255  prev->size = chunk_end - prev->offset;
256  }
257  ret = true;
258  }
259  }
260  return ret;
261 }
262 
263 void CfdpChunkList::insert(ChunkIdx i, const Chunk* chunk) {
264  ChunkIdx smallest_i;
265  Chunk* smallest_c;
266  bool next = combineNext(i, chunk);
267  bool combined;
268 
269  if (next) {
270  combined = combinePrevious(i, &m_chunks[i]);
271  if (combined) {
272  eraseChunk(i);
273  }
274  } else {
275  combined = combinePrevious(i, chunk);
276  if (!combined) {
277  if (m_count < m_maxChunks) {
278  insertChunk(i, chunk);
279  } else {
280  smallest_i = findSmallestSize();
281  smallest_c = &m_chunks[smallest_i];
282  if (smallest_c->size < chunk->size) {
283  eraseChunk(smallest_i);
284  insertChunk(findInsertPosition(chunk), chunk);
285  }
286  }
287  }
288  }
289 }
290 
291 ChunkIdx CfdpChunkList::findSmallestSize() const {
292  ChunkIdx i;
293  ChunkIdx smallest = 0;
294 
295  for (i = 1; i < m_count; ++i) {
296  if (m_chunks[i].size < m_chunks[smallest].size) {
297  smallest = i;
298  }
299  }
300 
301  return smallest;
302 }
303 
304 } // namespace Cfdp
305 } // namespace Ccsds
306 } // namespace Svc
void removeFromFirst(FileSize size)
Remove a specified size from the first chunk.
Definition: Chunk.cpp:79
void reset()
Reset the chunk list to empty state.
Definition: Chunk.cpp:58
FileSize size
The size of the chunk.
Definition: Chunk.hpp:51
void add(FileSize offset, FileSize size)
Add a chunk (file segment) to the list.
Definition: Chunk.cpp:63
FileSize offset
The start offset of the chunk within the file.
Definition: Chunk.hpp:50
U32 FileSize
File size and offset type.
void(*)(const Chunk *chunk, void *opaque) GapComputeCallback
Callback type for gap computation.
Definition: Chunk.hpp:75
CfdpChunkList(ChunkIdx maxChunks, Chunk *chunkMem)
Constructor - initializes chunk list with pre-allocated memory.
Definition: Chunk.cpp:51
RateGroupDivider component implementation.
const Chunk * getFirstChunk() const
Get the first chunk in the list.
Definition: Chunk.cpp:75
static FileSize CfdpChunkMax(FileSize a, FileSize b)
Selects the larger of the two passed-in offsets.
Definition: Chunk.hpp:61
#define FW_ASSERT(...)
Definition: Assert.hpp:14
U32 computeGaps(ChunkIdx maxGaps, FileSize total, FileSize start, GapComputeCallback callback, void *opaque) const
Compute gaps between chunks and invoke callback for each.
Definition: Chunk.cpp:94
Pairs an offset with a size to identify a specific piece of a file.
Definition: Chunk.hpp:49