• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package okio
17 
18 import kotlin.wasm.unsafe.withScopedMemoryAllocator
19 import okio.internal.ErrnoException
20 import okio.internal.fdClose
21 import okio.internal.preview1.fd
22 import okio.internal.preview1.fd_read
23 import okio.internal.preview1.size
24 import okio.internal.read
25 
26 internal class FileSource(
27   private val fd: fd,
28 ) : Source {
29   private val unsafeCursor = Buffer.UnsafeCursor()
30   private var closed = false
31 
32   override fun read(sink: Buffer, byteCount: Long): Long {
33     require(byteCount >= 0L) { "byteCount < 0: $byteCount" }
34     check(!closed) { "closed" }
35     val sinkInitialSize = sink.size
36 
37     // Request a writable segment in `sink`. We request at least 1024 bytes, unless the request is
38     // for smaller than that, in which case we request only that many bytes.
39     val cursor = sink.readAndWriteUnsafe(unsafeCursor)
40     val addedCapacityCount = cursor.expandBuffer(minByteCount = minOf(byteCount, 1024L).toInt())
41 
42     // Now that we have a writable segment, figure out how many bytes to read. This is the smaller
43     // of the user's requested byte count, and the segment's writable capacity.
44     val attemptCount = minOf(byteCount, addedCapacityCount).toInt()
45 
46     // Copy bytes from the file to the segment.
47     val bytesRead = fdRead(cursor.data!!, cursor.start, attemptCount)
48 
49     // Remove new capacity that was added but not used.
50     cursor.resizeBuffer(sinkInitialSize + bytesRead)
51     cursor.close()
52 
53     return when {
54       bytesRead == attemptCount -> bytesRead.toLong()
55       else -> if (bytesRead == 0) -1L else bytesRead.toLong()
56     }
57   }
58 
59   private fun fdRead(data: ByteArray, offset: Int, count: Int): size {
60     withScopedMemoryAllocator { allocator ->
61       val dataPointer = allocator.allocate(count)
62 
63       val iovec = allocator.allocate(8)
64       iovec.storeInt(dataPointer.address.toInt())
65       (iovec + 4).storeInt(count)
66 
67       val returnPointer = allocator.allocate(4) // `size` is u32, 4 bytes.
68       val errno = fd_read(
69         fd = fd,
70         iovs = iovec.address.toInt(),
71         iovsSize = 1,
72         returnPointer = returnPointer.address.toInt(),
73       )
74       if (errno != 0) throw ErrnoException(errno.toShort())
75 
76       val byteCount = returnPointer.loadInt()
77       if (byteCount != -1) {
78         dataPointer.read(data, offset, byteCount)
79       }
80 
81       return byteCount
82     }
83   }
84 
85   override fun timeout(): Timeout = Timeout.NONE
86 
87   override fun close() {
88     if (closed) return
89     closed = true
90     fdClose(fd)
91   }
92 }
93