diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 9334220be9d28e01972e60ed84bec52d044d4e8a..0eefd99c8c44babe7ddc7e38fb7f8813a214de30 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -663,6 +663,8 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/ani", "src/mod_fs/class_file", "src/mod_fs/class_file/ani", + "src/mod_fs/class_randomaccessfile", + "src/mod_fs/class_randomaccessfile/ani", "src/mod_fs/class_stat", "src/mod_fs/class_stat/ani", "src/mod_fs/properties", @@ -676,6 +678,8 @@ ohos_shared_library("ani_fs_class") { "src/mod_fs/ani/bind_function_class.cpp", "src/mod_fs/class_file/ani/file_ani.cpp", "src/mod_fs/class_file/fs_file.cpp", + "src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.cpp", + "src/mod_fs/class_randomaccessfile/fs_randomaccessfile.cpp", "src/mod_fs/class_stat/ani/stat_ani.cpp", "src/mod_fs/class_stat/fs_stat.cpp", "src/mod_fs/fs_utils.cpp", diff --git a/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp b/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp index 065495c9ade9c4c775ce494fa218d0c382077459..ad58e98c5f20bf29580d6cfbe1f6a79a2e61fce5 100644 --- a/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp +++ b/interfaces/kits/js/src/mod_fs/ani/bind_function_class.cpp @@ -27,6 +27,7 @@ #include "mkdir_ani.h" #include "move_ani.h" #include "open_ani.h" +#include "randomaccessfile_ani.h" #include "read_ani.h" #include "read_text_ani.h" #include "rmdir_ani.h" @@ -37,6 +38,21 @@ using namespace OHOS::FileManagement::ModuleFileIO::ANI; +static ani_status BindRafFileMethods(ani_env *env) +{ + static const char *className = "L@ohos/file/fs/RandomAccessFileInner;"; + + std::array methods = { + ani_native_function { "setFilePointer0", nullptr, + reinterpret_cast(RandomAccessFileAni::SetFilePointer) }, + ani_native_function { "close", nullptr, reinterpret_cast(RandomAccessFileAni::Close) }, + ani_native_function { "writeSync0", nullptr, reinterpret_cast(RandomAccessFileAni::WriteSync) }, + ani_native_function { "readSync0", nullptr, reinterpret_cast(RandomAccessFileAni::ReadSync) }, + }; + + return BindClass(env, className, methods); +} + static ani_status BindFileMethods(ani_env *env) { static const char *className = "L@ohos/file/fs/FileInner;"; @@ -129,6 +145,12 @@ ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) return status; }; + status = BindRafFileMethods(env); + if (status != ANI_OK) { + HILOGE("Cannot bind native methods for RafFile Class"); + return status; + }; + *result = ANI_VERSION_1; return ANI_OK; } diff --git a/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets b/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets index 67eb95add30a0cd803f0542ae46c90f870189651..34a4bdfb56bd06647a9f62472ed8b26ca4002408 100644 --- a/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets +++ b/interfaces/kits/js/src/mod_fs/ani/ets/@ohos.file.fs.ets @@ -516,10 +516,18 @@ export interface ReadTextOptions extends ReadOptions { encoding?: string; } -export interface WriteOptions { +export interface Options { + encoding?: string; +} + +export interface WriteOptions extends Options { offset?: number; length?: number; - encoding?: string; +} + +export interface RandomAccessFileOptions { + start?: number; + end?: number; } enum AccessModeType { @@ -533,6 +541,152 @@ enum AccessFlagType { LOCAL = 0, } +export interface RandomAccessFile { + fd: number; + filePointer: number; + + setFilePointer(filePointer: number): void; + close(): void; + write(buffer: ArrayBuffer | string, options?: WriteOptions): Promise; + write(buffer: ArrayBuffer | string, callback: AsyncCallback): void; + write(buffer: ArrayBuffer | string, options: WriteOptions, callback: AsyncCallback): void; + writeSync(buffer: ArrayBuffer | string, options?: WriteOptions): number; + read(buffer: ArrayBuffer, options?: ReadOptions): Promise; + read(buffer: ArrayBuffer, callback: AsyncCallback): void; + read(buffer: ArrayBuffer, options: ReadOptions, callback: AsyncCallback): void; + readSync(buffer: ArrayBuffer, options?: ReadOptions): number; +} + +class RandomAccessFileInner implements RandomAccessFile { + fd: number = -1; + filePointer: number = -1; + + private nativePtr: long = 0; + + constructor(ptr: long) { + if (this.nativePtr === 0) { + this.nativePtr = ptr; + } + } + + setFilePointer(filePointer: number): void { + this.setFilePointer0(filePointer); + this.filePointer = filePointer; + } + + native setFilePointer0(filePointer: number): void; + + native close(): void; + + writeSync(buffer: ArrayBuffer | string, options?: WriteOptions): number { + let length = options? this.writeSync0(buffer, options) : this.writeSync0(buffer); + this.filePointer += length; + return length; + } + + native writeSync0(buffer: ArrayBuffer | string, options?: WriteOptions): number; + + write(buffer: ArrayBuffer | string, options?: WriteOptions): Promise { + return new Promise((resolve: (result: number) => void, reject: (e: BusinessError) => void) => { + if (options === undefined) { + let promise = taskpool.execute(this.writeSync, buffer, options); + promise.then((ret: NullishType): void => { + let result = ret as number + resolve(result); + }).catch((e: BusinessError): void => { + reject(e); + }); + } else { + let promise = taskpool.execute(this.writeSync, buffer, options); + promise.then((ret: NullishType): void => { + let result = ret as number + resolve(result); + }).catch((e: BusinessError): void => { + reject(e); + }); + } + }); + } + + write(buffer: ArrayBuffer | string, options: WriteOptions, callback: AsyncCallback): void { + let promise = taskpool.execute(this.writeSync, buffer, options); + promise.then((ret: NullishType): void => { + let e = new BusinessError(); + e.code = 0; + let result = ret as number; + callback(e, result); + }).catch((e: BusinessError): void => { + callback(e, 0); + }); + } + + write(buffer: ArrayBuffer | string, callback: AsyncCallback): void { + let promise = taskpool.execute(this.writeSync, buffer); + promise.then((ret: NullishType): void => { + let e = new BusinessError(); + e.code = 0; + let result = ret as number; + callback(e, result); + }).catch((e: BusinessError): void => { + callback(e, 0); + }); + } + + readSync(buffer: ArrayBuffer, options?: ReadOptions): number { + const length = options? this.readSync0(buffer, options) : this.readSync0(buffer); + this.filePointer += length; + return length; + } + + native readSync0(buffer: ArrayBuffer, options?: ReadOptions): number; + + read(buffer: ArrayBuffer, options?: ReadOptions): Promise { + return new Promise((resolve: (result: number) => void, reject: (e: BusinessError) => void) => { + if (options === undefined) { + let promise = taskpool.execute(this.readSync, buffer); + promise.then((ret: NullishType): void => { + let result = ret as number; + resolve(result); + }).catch((e: BusinessError): void => { + reject(e); + }); + } else { + let promise = taskpool.execute(this.readSync, buffer, options); + promise.then((ret: NullishType): void => { + let result = ret as number; + resolve(result); + }).catch((e: BusinessError): void => { + reject(e); + }); + } + }); + } + + read(buffer: ArrayBuffer, options: ReadOptions, callback: AsyncCallback): void { + let promise = taskpool.execute(this.readSync, buffer, options); + promise.then((ret: NullishType): void => { + let e = new BusinessError(); + e.code = 0; + let result = ret as number; + callback(e, result); + }).catch((e: BusinessError): void => { + callback(e, 0); + }); + } + + read(buffer: ArrayBuffer, callback: AsyncCallback): void { + let promise = taskpool.execute(this.readSync, buffer); + promise.then((ret: NullishType): void => { + let e = new BusinessError(); + e.code = 0; + let result = ret as number; + callback(e, result); + }).catch((e: BusinessError): void => { + callback(e, 0); + }); + } +} + export interface File { fd: number; path: String; diff --git a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.cpp b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.cpp new file mode 100644 index 0000000000000000000000000000000000000000..36f30dad896fd81b72a4be4e7e80c0d242281c88 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "randomaccessfile_ani.h" + +#include "ani_helper.h" +#include "error_handler.h" +#include "filemgmt_libhilog.h" +#include "fs_randomaccessfile.h" +#include "type_converter.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { +using namespace OHOS::FileManagement::ModuleFileIO; +using namespace std; + +static FsRandomAccessFile *Unwrap(ani_env *env, ani_object object) +{ + ani_long nativePtr; + auto ret = env->Object_GetFieldByName_Long(object, "nativePtr", &nativePtr); + if (ret != ANI_OK) { + HILOGE("Unwrap FsRandomAccessFile err: %{private}d", ret); + return nullptr; + } + uintptr_t ptrValue = static_cast(nativePtr); + FsRandomAccessFile *rafFile = reinterpret_cast(ptrValue); + return rafFile; +} + +static tuple> ToReadOptions(ani_env *env, ani_object obj) +{ + ReadOptions options; + ani_boolean isUndefined; + env->Reference_IsUndefined(obj, &isUndefined); + if (isUndefined) { + return { true, nullopt }; + } + + auto [succOffset, offset] = AniHelper::ParseInt64Option(env, obj, "offset"); + if (!succOffset) { + HILOGE("Illegal option.offset parameter"); + return { false, nullopt }; + } + options.offset = offset; + + auto [succLength, length] = AniHelper::ParseInt64Option(env, obj, "length"); + if (!succLength) { + HILOGE("Illegal option.length parameter"); + return { false, nullopt }; + } + options.length = length; + return { true, make_optional(move(options)) }; +} + +static tuple> ToWriteOptions(ani_env *env, ani_object obj) +{ + WriteOptions options; + ani_boolean isUndefined; + env->Reference_IsUndefined(obj, &isUndefined); + if (isUndefined) { + return { true, nullopt }; + } + + auto [succOffset, offset] = AniHelper::ParseInt64Option(env, obj, "offset"); + if (!succOffset) { + HILOGE("Illegal option.offset parameter"); + return { false, nullopt }; + } + options.offset = offset; + + auto [succLength, length] = AniHelper::ParseInt64Option(env, obj, "length"); + if (!succLength) { + HILOGE("Illegal option.length parameter"); + return { false, nullopt }; + } + options.length = length; + + auto [succEncoding, encoding] = AniHelper::ParseEncoding(env, obj); + if (!succEncoding) { + HILOGE("Illegal option.encoding parameter"); + return { false, nullopt }; + } + options.encoding = encoding; + return { true, make_optional(move(options)) }; +} + +static tuple ParseStringBuffer(ani_env *env, const ani_object &buf) +{ + ani_class cls; + env->FindClass("Lstd/core/String;", &cls); + + ani_boolean isString; + env->Object_InstanceOf(buf, cls, &isString); + if (!isString) { + return { false, {} }; + } + auto result = static_cast(buf); + return { true, move(result) }; +} + +static tuple ParseArrayBuffer(ani_env *env, const ani_object &buf) +{ + ani_class cls; + env->FindClass("Lescompat/ArrayBuffer;", &cls); + + ani_boolean isArrayBuffer; + env->Object_InstanceOf(buf, cls, &isArrayBuffer); + if (!isArrayBuffer) { + return { false, {} }; + } + auto result = static_cast(buf); + return { true, move(result) }; +} + +void RandomAccessFileAni::SetFilePointer(ani_env *env, [[maybe_unused]] ani_object object, ani_double fp) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + auto ret = rafFile->SetFilePointerSync(static_cast(fp)); + if (!ret.IsSuccess()) { + HILOGE("SetFilePointerSync failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } +} + +void RandomAccessFileAni::Close(ani_env *env, [[maybe_unused]] ani_object object) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return; + } + auto ret = rafFile->CloseSync(); + if (!ret.IsSuccess()) { + HILOGE("close rafFile failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return; + } +} + +ani_double RandomAccessFileAni::WriteSync(ani_env *env, [[maybe_unused]] ani_object object, + ani_object buf, ani_object options) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return -1; + } + + auto [succOp, op] = ToWriteOptions(env, options); + if (!succOp) { + HILOGE("Failed to resolve options!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto [isString, stringBuffer] = ParseStringBuffer(env, buf); + if (isString) { + auto [succBuf, buffer] = TypeConverter::ToUTF8String(env, stringBuffer); + if (!succBuf) { + HILOGE("Failed to resolve stringBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + auto ret = rafFile->WriteSync(buffer, op); + if (!ret.IsSuccess()) { + HILOGE("write buffer failed!"); + ErrorHandler::Throw(env, ret.GetError()); + return -1; + } + return static_cast(ret.GetData().value()); + } + + auto [isArrayBuffer, arrayBuffer] = ParseArrayBuffer(env, buf); + if (isArrayBuffer) { + auto [succBuf, buffer] = TypeConverter::ToArrayBuffer(env, arrayBuffer); + if (!succBuf) { + HILOGE("Failed to resolve arrayBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + auto ret = rafFile->WriteSync(buffer, op); + if (!ret.IsSuccess()) { + HILOGE("write buffer failed!"); + ErrorHandler::Throw(env, ret.GetError()); + return -1; + } + return static_cast(ret.GetData().value()); + } + HILOGE("Unsupported buffer type!"); + ErrorHandler::Throw(env, EINVAL); + return -1; +} + +ani_double RandomAccessFileAni::ReadSync(ani_env *env, [[maybe_unused]] ani_object object, + ani_arraybuffer buf, ani_object options) +{ + auto rafFile = Unwrap(env, object); + if (rafFile == nullptr) { + HILOGE("Cannot unwrap rafFile!"); + ErrorHandler::Throw(env, UNKNOWN_ERR); + return -1; + } + + auto [succBuf, arrayBuffer] = TypeConverter::ToArrayBuffer(env, buf); + if (!succBuf) { + HILOGE("Failed to resolve arrayBuffer!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto [succOp, op] = ToReadOptions(env, options); + if (!succOp) { + HILOGE("Failed to resolve options!"); + ErrorHandler::Throw(env, EINVAL); + return -1; + } + + auto ret = rafFile-> ReadSync(arrayBuffer, op); + if (!ret.IsSuccess()) { + HILOGE("Read file content failed!"); + const auto &err = ret.GetError(); + ErrorHandler::Throw(env, err); + return -1; + } + return static_cast(ret.GetData().value()); +} + +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.h b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.h new file mode 100644 index 0000000000000000000000000000000000000000..7fc783bd5e2c902aa87a9f3d7270d318e3d79829 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/ani/randomaccessfile_ani.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_ANI_RANDOMACCESSFILE_ANI_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_ANI_RANDOMACCESSFILE_ANI_H + +#include + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace ANI { + +class RandomAccessFileAni final { +public: + static void SetFilePointer(ani_env *env, [[maybe_unused]] ani_object object, ani_double fp); + static void Close(ani_env *env, [[maybe_unused]] ani_object object); + static ani_double WriteSync(ani_env *env, [[maybe_unused]] ani_object object, ani_object buf, ani_object options); + static ani_double ReadSync(ani_env *env, [[maybe_unused]] ani_object object, + ani_arraybuffer buf, ani_object options); +}; +} // namespace ANI +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif //INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_ANI_RANDOMACCESSFILE_ANI_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/fs_randomaccessfile.cpp b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/fs_randomaccessfile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..87e48b46921fad99f0bc7680a3d455777e6bdfe1 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/fs_randomaccessfile.cpp @@ -0,0 +1,287 @@ +/* +* Copyright (c) 2025 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "fs_randomaccessfile.h" + +#include + +#include "file_uri.h" +#include "file_utils.h" +#include "filemgmt_libfs.h" +#include "filemgmt_libhilog.h" +#include "fs_utils.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +static int DoReadRAF(void* buf, size_t len, int fd, int64_t offset) +{ + unique_ptr readReq = { + new (nothrow) uv_fs_t, FsUtils::FsReqCleanup }; + if (readReq == nullptr) { + HILOGE("Failed to request heap memory."); + return ENOMEM; + } + uv_buf_t iov = uv_buf_init(static_cast(buf), len); + int ret = uv_fs_read(nullptr, readReq.get(), fd, &iov, 1, offset, nullptr); + return ret; +} + +static int DoWriteRAF(void* buf, size_t len, int fd, int64_t offset) +{ + unique_ptr writeReq = { + new (nothrow) uv_fs_t, FsUtils::FsReqCleanup }; + if (writeReq == nullptr) { + HILOGE("Failed to request heap memory."); + return ENOMEM; + } + uv_buf_t iov = uv_buf_init(static_cast(buf), len); + int ret = uv_fs_write(nullptr, writeReq.get(), fd, &iov, 1, offset, nullptr); + return ret; +} + +FsResult FsRandomAccessFile::GetFD() const +{ + if (!rafEntity) { + HILOGE("Failed to get entity of RandomAccessFile"); + return FsResult::Error(EIO); + } + return FsResult::Success(rafEntity->fd.get()->GetFD()); +} + +FsResult FsRandomAccessFile::GetFPointer() const +{ + if (!rafEntity) { + HILOGE("Failed to get entity of RandomAccessFile"); + return FsResult::Error(EIO); + } + return FsResult::Success(rafEntity->filePointer); +} + +FsResult FsRandomAccessFile::SetFilePointerSync(const int64_t &fp) const +{ + if (!rafEntity) { + HILOGE("Failed to get entity of RandomAccessFile"); + return FsResult::Error(EIO); + } + rafEntity->filePointer = fp; + return FsResult::Success(); +} + +static int64_t CalculateOffset(int64_t offset, int64_t fPointer) +{ + if (offset < 0) { + HILOGD("No specified offset provided"); + offset = fPointer; + } else { + offset += fPointer; + } + return offset; +} + +tuple ValidReadArg(ArrayBuffer &buffer, const optional &options) +{ + size_t retLen = 0; + int64_t offset = -1; + bool succ = false; + void *buf = buffer.buf; + size_t bufLen = buffer.length; + + if (bufLen > UINT_MAX) { + HILOGE("Invalid arraybuffer"); + return { false, nullptr, retLen, offset }; + } + optional lengthOp = nullopt; + optional offsetOp = nullopt; + if (options.has_value()) { + ReadOptions op = options.value(); + lengthOp = op.length; + offsetOp = op.offset; + } + tie(succ, retLen) = FsUtils::GetActualLen(bufLen, 0, lengthOp); + if (!succ) { + HILOGE("Failed to get actual length"); + return { false, nullptr, retLen, offset }; + } + if (offsetOp.has_value()) { + offset = offsetOp.value(); + if (offset < 0) { + HILOGE("option.offset shall be positive number"); + return { false, nullptr, retLen, offset }; + } + } + return { true, buf, retLen, offset }; +} + +FsResult FsRandomAccessFile::ReadSync(ArrayBuffer &buffer, const optional &options) const +{ + if (!rafEntity) { + HILOGE("Failed to get entity of RandomAccessFile"); + return FsResult::Error(EIO); + } + auto [succ, buf, len, offset] = ValidReadArg(buffer, options); + if (!succ) { + HILOGE("Invalid buffer/options"); + return FsResult::Error(EINVAL); + } + offset = CalculateOffset(offset, rafEntity->filePointer); + int actLen = DoReadRAF(buf, len, rafEntity->fd.get()->GetFD(), offset); + if (actLen < 0) { + HILOGE("Failed to read file for %{private}d", actLen); + return FsResult::Error(actLen); + } + rafEntity->filePointer = offset + actLen; + return FsResult::Success(static_cast(actLen)); +} + +tuple ValidWriteArg( + void *buffer, const size_t bufLen, const optional &options) +{ + size_t retLen = 0; + int64_t offset = -1; + bool succ = false; + + if (bufLen > UINT_MAX) { + HILOGE("The Size of buffer is too large"); + return { false, nullptr, 0, offset }; + } + + optional lengthOp = nullopt; + optional offsetOp = nullopt; + if (options.has_value()) { + WriteOptions op = options.value(); + lengthOp = op.length; + offsetOp = op.offset; + } + + tie(succ, retLen) = FsUtils::GetActualLen(bufLen, 0, lengthOp); + if (!succ) { + HILOGE("Failed to get actual length"); + return { false, nullptr, 0, offset }; + } + + if (offsetOp.has_value()) { + offset = offsetOp.value(); + if (offset < 0) { + HILOGE("option.offset shall be positive number"); + return { false, nullptr, 0, offset }; + } + } + return { true, buffer, retLen, offset }; +} + +FsResult FsRandomAccessFile::WriteSync(const string &buffer, const optional &options) const +{ + if (!rafEntity) { + HILOGE("Failed to get entity of RandomAccessFile"); + return FsResult::Error(EIO); + } + + bool succ = false; + size_t len = 0; + int64_t offset = -1; + void *buf = nullptr; + size_t bufLen = static_cast(buffer.length()); + + tie(succ, buf, len, offset) = ValidWriteArg(const_cast(static_cast(buffer.c_str())), + bufLen, options); + if (!succ) { + HILOGE("Invalid buffer/options"); + return FsResult::Error(EINVAL); + } + offset = CalculateOffset(offset, rafEntity->filePointer); + int writeLen = DoWriteRAF(buf, len, rafEntity->fd.get()->GetFD(), offset); + if (writeLen < 0) { + return FsResult::Error(writeLen); + } + rafEntity->filePointer = offset + writeLen; + return FsResult::Success(static_cast(writeLen)); +} + +FsResult FsRandomAccessFile::WriteSync(const ArrayBuffer &buffer, const optional &options) const +{ + if (!rafEntity) { + HILOGE("Failed to get entity of RandomAccessFile"); + return FsResult::Error(EIO); + } + + bool succ = false; + size_t len = 0; + int64_t offset = -1; + void *buf = nullptr; + + tie(succ, buf, len, offset) = ValidWriteArg(buffer.buf, buffer.length, options); + if (!succ) { + HILOGE("Invalid buffer/options"); + return FsResult::Error(EINVAL); + } + offset = CalculateOffset(offset, rafEntity->filePointer); + int writeLen = DoWriteRAF(buf, len, rafEntity->fd.get()->GetFD(), offset); + if (writeLen < 0) { + return FsResult::Error(writeLen); + } + rafEntity->filePointer = offset + writeLen; + return FsResult::Success(static_cast(writeLen)); +} + +static int CloseFd(int fd) +{ + unique_ptr closeReq = { new uv_fs_t, FsUtils::FsReqCleanup }; + if (!closeReq) { + HILOGE("Failed to request heap memory."); + return ENOMEM; + } + int ret = uv_fs_close(nullptr, closeReq.get(), fd, nullptr); + if (ret < 0) { + HILOGE("Failed to close file with ret: %{private}d", ret); + return ret; + } + return ERRNO_NOERR; +} + +FsResult FsRandomAccessFile::CloseSync() const +{ + if (!rafEntity) { + HILOGE("Failed to get entity of RandomAccessFile"); + return FsResult::Error(EIO); + } + auto err = CloseFd(rafEntity->fd.get()->GetFD()); + if (err) { + return FsResult::Error(err); + } + return FsResult::Success(); +} + +FsResult FsRandomAccessFile::Constructor() +{ + auto rafEntity = CreateUniquePtr(); + if (rafEntity == nullptr) { + HILOGE("Failed to request heap memory."); + return FsResult::Error(ENOMEM); + } + + FsRandomAccessFile *randomAccessFilePtr = new FsRandomAccessFile(move(rafEntity)); + if (randomAccessFilePtr == nullptr) { + HILOGE("INNER BUG. Failed to wrap entity for obj RandomAccessFile"); + return FsResult::Error(EIO); + } + return FsResult::Success(move(randomAccessFilePtr)); +} + +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/fs_randomaccessfile.h b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/fs_randomaccessfile.h new file mode 100644 index 0000000000000000000000000000000000000000..abeb9a02c48b45dab5a9ae92ab128b1ec92edcf2 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/fs_randomaccessfile.h @@ -0,0 +1,86 @@ +/* +* Copyright (c) 2025 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_FS_RANDOMACCESSFILE_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_FS_RANDOMACCESSFILE_H + +#include "randomaccessfile_entity.h" +#include "filemgmt_libfs.h" + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +using namespace std; + +struct WriteOptions { + optional length = nullopt; + optional offset = nullopt; + optional encoding = nullopt; +}; + +struct ReadOptions { + optional offset = nullopt; + optional length = nullopt; +}; + +class FsRandomAccessFile { +public: + inline static const string className_ = "RandomAccessFile"; + + RandomAccessFileEntity *GetRAFEntity() const + { + return rafEntity.get(); + } + + FsRandomAccessFile(const FsRandomAccessFile &other) = delete; + FsRandomAccessFile &operator=(const FsRandomAccessFile &other) = delete; + + FsRandomAccessFile(FsRandomAccessFile &&other) noexcept : rafEntity(move(other.rafEntity)) + { + other.rafEntity = nullptr; + } + + FsRandomAccessFile &operator=(FsRandomAccessFile &&other) noexcept + { + if (this != &other) { + rafEntity = move(other.rafEntity); + other.rafEntity = nullptr; + } + return *this; + } + + ~FsRandomAccessFile() = default; + + FsResult SetFilePointerSync(const int64_t &fp) const; + FsResult WriteSync(const string &buffer, const optional &options = nullopt) const; + FsResult WriteSync(const ArrayBuffer &buffer, const optional &options = nullopt) const; + FsResult ReadSync(ArrayBuffer &buffer, const optional &options = nullopt) const; + FsResult CloseSync() const; + + FsResult GetFD() const; + FsResult GetFPointer() const; + + static FsResult Constructor(); + +private: + unique_ptr rafEntity; + explicit FsRandomAccessFile(unique_ptr entity) : rafEntity(move(entity)) {} +}; +const string readProcedureName = "FileIORandomAccessFileRead"; +const string writeProcedureName = "FileIORandomAccessFileWrite"; +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS +#endif //INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_FS_RANDOMACCESSFILE_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/randomaccessfile_entity.h b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/randomaccessfile_entity.h index 3f5b12a7f268a3781cbc5031adb9c3c11591d066..6d80bebec09effa4bda3e0ab6f2178df139e9b89 100644 --- a/interfaces/kits/js/src/mod_fs/class_randomaccessfile/randomaccessfile_entity.h +++ b/interfaces/kits/js/src/mod_fs/class_randomaccessfile/randomaccessfile_entity.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Huawei Device Co., Ltd. + * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,17 +16,21 @@ #ifndef INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_RANDOMACCESSFILE_ENTITY_H #define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_RANDOMACCESSFILE_ENTITY_H +#include +#include #include #include "fd_guard.h" -#include "n_val.h" +#include "filemgmt_libhilog.h" namespace OHOS { namespace FileManagement { namespace ModuleFileIO { +using namespace std; + const int64_t INVALID_POS = -1; struct RandomAccessFileEntity { - std::unique_ptr fd = {nullptr}; + unique_ptr fd = {nullptr}; int64_t filePointer = 0; int64_t start = INVALID_POS; int64_t end = INVALID_POS; @@ -34,4 +38,4 @@ struct RandomAccessFileEntity { } // namespace ModuleFileIO } // namespace FileManagement } // namespace OHOS -#endif \ No newline at end of file +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_RANDOMACCESSFILE_RANDOMACCESSFILE_ENTITY_H \ No newline at end of file