Generate SSD truth store storage accounting from typed Jerboa
ober
e2a28216871b513244abec1ae1652e17a844582f
--- a/templates/ssd-review.ss +++ b/templates/ssd-review.ss @@ -15539,7 +15539,9 @@ truthStoreReadLocalText truthStoreTruthPath truthStoreLoadTruth truthStoreSaveSession truthStoreAppendEvent truthStoreApplyTruth - truthStoreStorageSummaryLocal) + truthStoreStorageSummaryLocal + truthStoreStorageStatsLocal + truthStoreEnsureStorageCapacityLocal) (type Exception) (type File) (type InputStream) @@ -15548,8 +15550,12 @@ (type MessageDigest) (type Path) (type SsdSession) + (type StorageStats) (type TruthStore) (type JSONObject) + (extern (truthStoreRoot + (store : TruthStore)) : File + (kotlin-member-get root)) (extern (truthStoreGroundTruthDir (store : TruthStore)) : File (kotlin-member-get groundTruthDir)) @@ -15603,6 +15609,9 @@ (extern (truthStoreIllegalArgumentException (message : String)) : Exception (kotlin-call IllegalArgumentException)) + (extern (truthStoreIllegalStateException + (message : String)) : Exception + (kotlin-call IllegalStateException)) (extern (truthStoreMessageDigestGetInstance (algorithm : String)) : MessageDigest (kotlin-call MessageDigest getInstance)) @@ -15612,6 +15621,10 @@ (kotlin-member-call digest)) (extern (truthStoreMaxZipEntryBytes) : Int (kotlin-value MAX_ZIP_ENTRY_BYTES)) + (extern (truthStoreMaxStorageBytes) : Int + (kotlin-value MAX_STORAGE_BYTES)) + (extern (truthStoreMaxStorageFiles) : Int32 + (kotlin-value MAX_STORAGE_FILES)) (def (truthStoreSha256Hex (bytes : Bytes)) : String (bytesToLowerHex (truthStoreMessageDigestDigest @@ -15742,7 +15755,90 @@ (truthStoreCountTruthFiles (truthStoreListFiles (truthStoreGroundTruthDir store))) (truthStoreCountFiles - (truthStoreListFiles (truthStoreEventsDir store))))))) + (truthStoreListFiles (truthStoreEventsDir store))))) + (def (truthStoreAccumulateStorageStats! + (file : File) + (stats : StorageStats) + (depth : Int32)) : Unit + (begin + (if (treeDepthExceeded depth (int32 32)) + (throw + (truthStoreIllegalStateException "Storage tree too deep") + Unit) + (begin)) + (if (fileIsSymbolicLink file) + (throw + (truthStoreIllegalStateException "Storage symlink rejected") + Unit) + (begin)) + (if (fileIsDirectory file) + (let ((children (truthStoreListFiles file))) + (if (nullable-null? children) + (begin) + (forEachFileArray + (nullable-get children) + (lambda ((child : File)) + (truthStoreAccumulateStorageStats! + child + stats + (+ depth (int32 1))))))) + (if (fileIsRegular file) + (begin + (StorageStats-files-set! + stats + (+ (StorageStats-files stats) (int32 1))) + (StorageStats-bytes-set! + stats + (+ (StorageStats-bytes stats) (fileSizeBytes file))) + (if (storageStatsExceedsQuota + (StorageStats-files stats) + (StorageStats-bytes stats) + (truthStoreMaxStorageFiles) + (truthStoreMaxStorageBytes)) + (throw + (truthStoreIllegalStateException + "Storage quota exceeded") + Unit) + (begin))) + (begin))))) + (def (truthStoreStorageStatsLocal (store : TruthStore)) : StorageStats + (let ((stats (make-StorageStats (int32 0) (int 0)))) + (begin + (truthStoreAccumulateStorageStats! + (truthStoreRoot store) + stats + (int32 0)) + stats))) + (def (truthStoreEnsureStorageCapacityLocal + (store : TruthStore) + (destination : File) + (bytes : Int)) : Unit + (begin + (if (storageEntryBytesAllowed + bytes + (truthStoreMaxZipEntryBytes)) + (begin) + (throw + (truthStoreIllegalArgumentException + "Per-file storage limit exceeded") + Unit)) + (let ((stats (truthStoreStorageStatsLocal store))) + (let ((oldBytes (oldDestinationBytes destination)) + (newFiles + (+ (StorageStats-files stats) + (destinationNewFileDelta destination)))) + (let ((newBytes + (+ (- (StorageStats-bytes stats) oldBytes) bytes))) + (if (storageCapacityWithinQuota + newFiles + newBytes + (truthStoreMaxStorageFiles) + (truthStoreMaxStorageBytes)) + (begin) + (throw + (truthStoreIllegalArgumentException + "Storage quota exceeded") + Unit))))))))) (kotlin-file-lines "com/sfb/ssdreview/TruthStore.kt" ( @@ -15789,7 +15885,7 @@ "import kotlin.concurrent.thread" "" "class TruthStore(private val context: Context) {" - " private val root = File(context.filesDir, \"ssd_review\")" + " internal val root = File(context.filesDir, \"ssd_review\")" " internal val groundTruthDir = File(root, \"ground_truth\")" " private val learnedDir = File(root, \"learned\")" " internal val eventsDir = File(root, \"events\")" @@ -15884,39 +15980,11 @@ " private fun containedLeaf(base: File, leaf: String): File =" " truthStoreContainedLeaf(base, leaf)" "" - " private fun storageStats(): StorageStats {" - " val stats = StorageStats(0, 0L)" - " fun walk(file: File, depth: Int) {" - " if (treeDepthExceeded(depth, 32)) throw IllegalStateException(\"Storage tree too deep\")" - " if (fileIsSymbolicLink(file)) throw IllegalStateException(\"Storage symlink rejected\")" - " if (fileIsDirectory(file)) {" - " if (fileListFilesPresent(file)) {" - " forEachFileArray(checkNotNull(file.listFiles())) { child ->" - " walk(child, depth + 1)" - " }" - " }" - " } else if (fileIsRegular(file)) {" - " stats.files += 1" - " stats.bytes += fileSizeBytes(file)" - " if (storageStatsExceedsQuota(stats.files, stats.bytes, MAX_STORAGE_FILES, MAX_STORAGE_BYTES)) {" - " throw IllegalStateException(\"Storage quota exceeded\")" - " }" - " }" - " }" - " walk(root, 0)" - " return stats" - " }" + " private fun storageStats(): StorageStats =" + " truthStoreStorageStatsLocal(this)" "" - " private fun ensureStorageCapacity(destination: File, bytes: Long) {" - " require(storageEntryBytesAllowed(bytes, MAX_ZIP_ENTRY_BYTES)) { \"Per-file storage limit exceeded\" }" - " val stats = storageStats()" - " val oldBytes = oldDestinationBytes(destination)" - " val newFiles = stats.files + destinationNewFileDelta(destination)" - " val newBytes = stats.bytes - oldBytes + bytes" - " require(storageCapacityWithinQuota(newFiles, newBytes, MAX_STORAGE_FILES, MAX_STORAGE_BYTES)) {" - " \"Storage quota exceeded\"" - " }" - " }" + " private fun ensureStorageCapacity(destination: File, bytes: Long) =" + " truthStoreEnsureStorageCapacityLocal(this, destination, bytes)" "" " private fun atomicWrite(destination: File, bytes: ByteArray) {" " ensureStorageCapacity(destination, byteArraySizeBytes(bytes))"