Skip to content

Commit 52f5310

Browse files
committed
Introduce a ByteCount abstraction for representing bytes
Rather than inconsistently using Int and Int64 throughout the codebase.
1 parent d66b9ec commit 52f5310

File tree

8 files changed

+131
-41
lines changed

8 files changed

+131
-41
lines changed

Sources/SWBCore/LibclangVendored/Libclang.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ public final class ClangCASDatabases {
470470
libclang_casdatabases_dispose(dbs)
471471
}
472472

473-
public func getOndiskSize() throws -> Int? {
473+
public func getOndiskSize() throws -> Int64? {
474474
var error: ClangCASDatabases.Error? = nil
475475
let ret = libclang_casdatabases_get_ondisk_size(dbs, { c_error in
476476
error = .operationFailed(String(cString: c_error!))
@@ -481,12 +481,12 @@ public final class ClangCASDatabases {
481481
if ret < 0 {
482482
return nil
483483
}
484-
return Int(ret)
484+
return ret
485485
}
486486

487-
public func setOndiskSizeLimit(_ limit: Int?) throws {
487+
public func setOndiskSizeLimit(_ limit: Int64?) throws {
488488
var error: ClangCASDatabases.Error? = nil
489-
libclang_casdatabases_set_ondisk_size_limit(dbs, Int64(limit ?? 0), { c_error in
489+
libclang_casdatabases_set_ondisk_size_limit(dbs, limit ?? 0, { c_error in
490490
error = .operationFailed(String(cString: c_error!))
491491
})
492492
if let error {

Sources/SWBCore/Settings/CASOptions.swift

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public struct CASOptions: Hashable, Serializable, Encodable, Sendable {
3434
/// Cache directory is removed after the build is finished.
3535
case discarded
3636
/// The maximum size for the cache directory in bytes. `nil` means no limit.
37-
case maxSizeBytes(Int?)
37+
case maxSizeBytes(ByteCount?)
3838
/// The maximum size for the cache directory, in terms of percentage of the
3939
/// available space on the disk. Set to 100 to indicate no limit, 50 to
4040
/// indicate that the cache size will not be left over half the available disk
@@ -86,26 +86,22 @@ public struct CASOptions: Hashable, Serializable, Encodable, Sendable {
8686
/// * "0": indicates no limit
8787
///
8888
/// Returns `nil` if the string is invalid.
89-
public static func parseSizeLimit(_ sizeLimitStr: String) -> Int? {
90-
if let size = Int(sizeLimitStr) {
89+
public static func parseSizeLimit(_ sizeLimitStr: String) -> ByteCount? {
90+
if let size = ByteCount(Int64(sizeLimitStr)) {
9191
return size
9292
}
93-
guard let size = Int(sizeLimitStr.dropLast()) else {
93+
guard let size = Int64(sizeLimitStr.dropLast()) else {
9494
return nil
9595
}
96-
let kb = 1024
97-
let mb = kb * 1024
98-
let gb = mb * 1024
99-
let tb = gb * 1024
100-
switch sizeLimitStr.last! {
101-
case "K": // kilobytes
102-
return size * kb
103-
case "M": // megabytes
104-
return size * mb
105-
case "G": // gigabytes
106-
return size * gb
107-
case "T": // terabytes
108-
return size * tb
96+
switch sizeLimitStr.last {
97+
case "K":
98+
return .kilobytes(size)
99+
case "M":
100+
return .megabytes(size)
101+
case "G":
102+
return .gigabytes(size)
103+
case "T":
104+
return .terabytes(size)
109105
default:
110106
return nil
111107
}
@@ -228,7 +224,7 @@ public struct CASOptions: Hashable, Serializable, Encodable, Sendable {
228224
guard let sizeLimit = CASOptions.parseSizeLimit(sizeLimitStr) else {
229225
throw Errors.invalidSizeLimit(sizeLimitString: sizeLimitStr, origin: origin)
230226
}
231-
return .maxSizeBytes(sizeLimit > 0 ? sizeLimit : nil)
227+
return .maxSizeBytes(sizeLimit > .zero ? sizeLimit : nil)
232228
}
233229

234230
let sizeLimitStr = scope.evaluate(BuiltinMacros.COMPILATION_CACHE_LIMIT_SIZE)

Sources/SWBTaskExecution/DynamicTaskSpecs/CompilationCachingDataPruner.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ package final class CompilationCachingDataPruner: Sendable {
112112
{ activityID in
113113
let status: BuildOperationTaskEnded.Status
114114
do {
115-
let dbSize = try casDBs.getOndiskSize()
115+
let dbSize = try ByteCount(casDBs.getOndiskSize())
116116
let sizeLimit = try computeCASSizeLimit(casOptions: casOpts, dbSize: dbSize, fileSystem: fs)
117117
if let dbSize, let sizeLimit, sizeLimit < dbSize {
118118
activityReporter.emit(
@@ -125,7 +125,7 @@ package final class CompilationCachingDataPruner: Sendable {
125125
signature: signature
126126
)
127127
}
128-
try casDBs.setOndiskSizeLimit(sizeLimit ?? 0)
128+
try casDBs.setOndiskSizeLimit(sizeLimit?.count ?? 0)
129129
try casDBs.pruneOndiskData()
130130
status = .succeeded
131131
} catch {
@@ -181,8 +181,8 @@ package final class CompilationCachingDataPruner: Sendable {
181181
{ activityID in
182182
let status: BuildOperationTaskEnded.Status
183183
do {
184-
let dbSize = try casDBs.getStorageSize()
185-
let sizeLimit = try computeCASSizeLimit(casOptions: casOpts, dbSize: dbSize.map{Int($0)}, fileSystem: fs)
184+
let dbSize = try ByteCount(casDBs.getStorageSize())
185+
let sizeLimit = try computeCASSizeLimit(casOptions: casOpts, dbSize: dbSize, fileSystem: fs)
186186
if let dbSize, let sizeLimit, sizeLimit < dbSize {
187187
activityReporter.emit(
188188
diagnostic: Diagnostic(
@@ -194,7 +194,7 @@ package final class CompilationCachingDataPruner: Sendable {
194194
signature: signature
195195
)
196196
}
197-
try casDBs.setSizeLimit(Int64(sizeLimit ?? 0))
197+
try casDBs.setSizeLimit(sizeLimit?.count ?? 0)
198198
try casDBs.prune()
199199
status = .succeeded
200200
} catch {
@@ -250,8 +250,8 @@ package final class CompilationCachingDataPruner: Sendable {
250250
{ activityID in
251251
let status: BuildOperationTaskEnded.Status
252252
do {
253-
let dbSize = (try? toolchainCAS.getOnDiskSize()).map { Int($0) }
254-
let sizeLimit = try computeCASSizeLimit(casOptions: casOpts, dbSize: dbSize, fileSystem: fs).map { Int64($0) }
253+
let dbSize = try? ByteCount(toolchainCAS.getOnDiskSize())
254+
let sizeLimit = try computeCASSizeLimit(casOptions: casOpts, dbSize: dbSize, fileSystem: fs)
255255
if let dbSize, let sizeLimit, sizeLimit < dbSize {
256256
activityReporter.emit(
257257
diagnostic: Diagnostic(
@@ -263,7 +263,7 @@ package final class CompilationCachingDataPruner: Sendable {
263263
signature: signature
264264
)
265265
}
266-
try toolchainCAS.setOnDiskSizeLimit(sizeLimit ?? 0)
266+
try toolchainCAS.setOnDiskSizeLimit(sizeLimit?.count ?? 0)
267267
try toolchainCAS.prune()
268268
status = .succeeded
269269
} catch {
@@ -287,9 +287,9 @@ package final class CompilationCachingDataPruner: Sendable {
287287

288288
fileprivate func computeCASSizeLimit(
289289
casOptions: CASOptions,
290-
dbSize: Int?,
290+
dbSize: ByteCount?,
291291
fileSystem fs: any FSProxy
292-
) throws -> Int? {
292+
) throws -> ByteCount? {
293293
guard let dbSize else { return nil }
294294
switch casOptions.limitingStrategy {
295295
case .discarded:
@@ -304,6 +304,6 @@ fileprivate func computeCASSizeLimit(
304304
return nil
305305
}
306306
let availableSpace = dbSize + freeSpace
307-
return availableSpace * percent / 100
307+
return ByteCount(availableSpace.count * Int64(percent) / 100)
308308
}
309309
}

Sources/SWBTaskExecution/DynamicTaskSpecs/SwiftDriverJobDynamicTaskSpec.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ final class SwiftDriverJobDynamicTaskSpec: DynamicTaskSpec {
242242
// rdar://91295617 (Swift produces empty serialized diagnostics if there are none which is not parseable by clang_loadDiagnostics)
243243
return expectedDiagnostics.filter { filePath in
244244
do {
245-
let shouldAdd = try fs.exists(filePath) && (try fs.getFileSize(filePath)) > 0
245+
let shouldAdd = try fs.exists(filePath) && (try fs.getFileSize(filePath)) > .zero
246246
return shouldAdd
247247
} catch {
248248
return false

Sources/SWBUtil/ByteCount.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public struct ByteCount: Hashable, Sendable {
14+
public var count: Int64
15+
16+
public init?(_ count: Int64?) {
17+
guard let count else { return nil }
18+
self.count = count
19+
}
20+
21+
public init(_ count: Int64) {
22+
self.count = count
23+
}
24+
}
25+
26+
extension ByteCount: Codable {
27+
public init(from decoder: any Swift.Decoder) throws {
28+
self.count = try .init(from: decoder)
29+
}
30+
31+
public func encode(to encoder: any Swift.Encoder) throws {
32+
try self.count.encode(to: encoder)
33+
}
34+
}
35+
36+
extension ByteCount: Serializable {
37+
public init(from deserializer: any Deserializer) throws {
38+
self.count = try .init(from: deserializer)
39+
}
40+
41+
public func serialize<T>(to serializer: T) where T : Serializer {
42+
self.count.serialize(to: serializer)
43+
}
44+
}
45+
46+
extension ByteCount: Comparable {
47+
public static func < (lhs: ByteCount, rhs: ByteCount) -> Bool {
48+
lhs.count < rhs.count
49+
}
50+
}
51+
52+
extension ByteCount: AdditiveArithmetic {
53+
public static var zero: ByteCount {
54+
Self(0)
55+
}
56+
57+
public static func + (lhs: ByteCount, rhs: ByteCount) -> ByteCount {
58+
Self(lhs.count + rhs.count)
59+
}
60+
61+
public static func - (lhs: ByteCount, rhs: ByteCount) -> ByteCount {
62+
Self(lhs.count - rhs.count)
63+
}
64+
}
65+
66+
extension ByteCount: CustomStringConvertible {
67+
public var description: String {
68+
"\(count) bytes"
69+
}
70+
}
71+
72+
extension ByteCount {
73+
private static let kb = Int64(1024)
74+
private static let mb = kb * 1024
75+
private static let gb = mb * 1024
76+
private static let tb = gb * 1024
77+
78+
public static func kilobytes(_ count: Int64) -> Self {
79+
Self(kb * count)
80+
}
81+
82+
public static func megabytes(_ count: Int64) -> Self {
83+
Self(mb * count)
84+
}
85+
86+
public static func gigabytes(_ count: Int64) -> Self {
87+
Self(gb * count)
88+
}
89+
90+
public static func terabytes(_ count: Int64) -> Self {
91+
Self(tb * count)
92+
}
93+
}

Sources/SWBUtil/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_library(SWBUtil
1919
AsyncOperationQueue.swift
2020
AsyncSingleValueCache.swift
2121
AsyncStreamController.swift
22+
ByteCount.swift
2223
ByteString.swift
2324
Cache.swift
2425
Collection.swift

Sources/SWBUtil/FSProxy.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ public protocol FSProxy: AnyObject, Sendable {
255255
func isOnPotentiallyRemoteFileSystem(_ path: Path) -> Bool
256256

257257
/// Returns the free disk space of the volume of `path` in bytes, or `nil` if the underlying FS implementation doesn't support this.
258-
func getFreeDiskSpace(_ path: Path) throws -> Int?
258+
func getFreeDiskSpace(_ path: Path) throws -> ByteCount?
259259
}
260260

261261
public extension FSProxy {
@@ -287,7 +287,7 @@ public extension FSProxy {
287287
return false
288288
}
289289

290-
func getFreeDiskSpace(_ path: Path) throws -> Int? {
290+
func getFreeDiskSpace(_ path: Path) throws -> ByteCount? {
291291
return nil
292292
}
293293

@@ -296,8 +296,8 @@ public extension FSProxy {
296296
return isSymlink(path, &exists)
297297
}
298298

299-
func getFileSize(_ path: Path) throws -> Int64 {
300-
try Int64(getFileInfo(path).statBuf.st_size)
299+
func getFileSize(_ path: Path) throws -> ByteCount {
300+
try ByteCount(Int64(getFileInfo(path).statBuf.st_size))
301301
}
302302
}
303303

@@ -860,12 +860,12 @@ class LocalFS: FSProxy, @unchecked Sendable {
860860
#endif
861861
}
862862

863-
func getFreeDiskSpace(_ path: Path) throws -> Int? {
863+
func getFreeDiskSpace(_ path: Path) throws -> ByteCount? {
864864
let systemAttributes = try fileManager.attributesOfFileSystem(forPath: path.str)
865865
guard let freeSpace = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value else {
866866
return nil
867867
}
868-
return Int(freeSpace)
868+
return ByteCount(freeSpace)
869869
}
870870
}
871871

Tests/SWBUtilTests/MsgPackSerializationTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ import SWBUtil
371371
"one": IntRange(uncheckedBounds: (1, 4)),
372372
"two": IntRange(uncheckedBounds: (-13, -2)),
373373
"three": IntRange(uncheckedBounds: (-99, -3)),
374-
"infinity": IntRange(uncheckedBounds: (0, 43793218932)),
374+
"infinity": IntRange(uncheckedBounds: (0, 2147483647)),
375375
]
376376

377377
// Serialize!

0 commit comments

Comments
 (0)