shrink resizes the data to a smaller length. Requirement: from > to. If truncates are done on block boundaries, this is reasonably efficient. However, if truncating in the middle of a block, we need to fetch the block first, truncate it, and write it again.
(e sqlExecutor, inodeID, from, to uint64)
| 78 | // efficient. However, if truncating in the middle of a block, |
| 79 | // we need to fetch the block first, truncate it, and write it again. |
| 80 | func shrink(e sqlExecutor, inodeID, from, to uint64) error { |
| 81 | delRange := newBlockRange(to, from-to) |
| 82 | deleteFrom := delRange.start |
| 83 | |
| 84 | if delRange.startOffset > 0 { |
| 85 | // We're truncating in the middle of a block, fetch it, truncate its |
| 86 | // data, and write it again. |
| 87 | // TODO(marc): this would be more efficient if we had LEFT for bytes. |
| 88 | data, err := getBlockData(e, inodeID, delRange.start) |
| 89 | if err != nil { |
| 90 | return err |
| 91 | } |
| 92 | data = data[:delRange.startOffset] |
| 93 | if err := updateBlockData(e, inodeID, delRange.start, data); err != nil { |
| 94 | return err |
| 95 | } |
| 96 | // We don't need to delete this block. |
| 97 | deleteFrom++ |
| 98 | } |
| 99 | |
| 100 | deleteTo := delRange.last |
| 101 | if delRange.lastLength == 0 { |
| 102 | // The last block did not previously exist. |
| 103 | deleteTo-- |
| 104 | } |
| 105 | if deleteTo < deleteFrom { |
| 106 | return nil |
| 107 | } |
| 108 | |
| 109 | // There is something to delete. |
| 110 | // TODO(marc): would it be better to pass the block IDs? |
| 111 | delStmt := `DELETE FROM fs_BLOCK WHERE id = ? AND block >= ?` |
| 112 | if _, err := e.Exec(delStmt, inodeID, deleteFrom); err != nil { |
| 113 | return err |
| 114 | } |
| 115 | |
| 116 | return nil |
| 117 | } |
| 118 | |
| 119 | // grow resizes the data to a larger length. |
| 120 | // Requirement: to > from. |