1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "read_lines.h"
16
17 #include <unistd.h>
18
19 #include "class_readeriterator/readeriterator_entity.h"
20 #include "class_readeriterator/readeriterator_n_exporter.h"
21 #include "common_func.h"
22 #include "file_utils.h"
23 #include "filemgmt_libhilog.h"
24 #include "rust_file.h"
25
26 namespace OHOS {
27 namespace FileManagement {
28 namespace ModuleFileIO {
29 using namespace std;
30 using namespace OHOS::FileManagement::LibN;
31
CheckOptionArg(napi_env env,napi_value argOption)32 static int CheckOptionArg(napi_env env, napi_value argOption)
33 {
34 NVal option(env, argOption);
35 if (option.HasProp("encoding")) {
36 auto [succ, encoding, ignore] = option.GetProp("encoding").ToUTF8String("utf-8");
37 string_view encodingStr(encoding.get());
38 if (!succ || encodingStr != "utf-8") {
39 return EINVAL;
40 }
41 }
42
43 return ERRNO_NOERR;
44 }
45
GetFileSize(const string & path,int64_t & offset)46 static int GetFileSize(const string &path, int64_t &offset)
47 {
48 std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> stat_req = {
49 new (std::nothrow) uv_fs_t, CommonFunc::fs_req_cleanup };
50 if (!stat_req) {
51 HILOGE("Failed to request heap memory.");
52 return ENOMEM;
53 }
54
55 int ret = uv_fs_stat(nullptr, stat_req.get(), path.c_str(), nullptr);
56 if (ret < 0) {
57 HILOGE("Failed to get file stat by path");
58 return ret;
59 }
60
61 offset = static_cast<int64_t>(stat_req->statbuf.st_size);
62 return ERRNO_NOERR;
63 }
64
InstantiateReaderIterator(napi_env env,void * iterator,int64_t offset,bool async=false)65 static NVal InstantiateReaderIterator(napi_env env, void *iterator, int64_t offset, bool async = false)
66 {
67 if (iterator == nullptr) {
68 HILOGE("Invalid argument iterator");
69 if (async) {
70 return {env, NError(EINVAL).GetNapiErr(env)};
71 }
72 NError(EINVAL).ThrowErr(env);
73 return NVal();
74 }
75
76 napi_value objReaderIterator = NClass::InstantiateClass(env, ReaderIteratorNExporter::className_, {});
77 if (!objReaderIterator) {
78 HILOGE("Failed to instantiate class ReaderIterator");
79 if (async) {
80 return {env, NError(UNKROWN_ERR).GetNapiErr(env)};
81 }
82 NError(UNKROWN_ERR).ThrowErr(env);
83 return NVal();
84 }
85
86 auto readerIteratorEntity = NClass::GetEntityOf<ReaderIteratorEntity>(env, objReaderIterator);
87 if (!readerIteratorEntity) {
88 HILOGE("Failed to get readerIteratorEntity");
89 if (async) {
90 return {env, NError(UNKROWN_ERR).GetNapiErr(env)};
91 }
92 NError(UNKROWN_ERR).ThrowErr(env);
93 return NVal();
94 }
95
96 readerIteratorEntity->iterator = iterator;
97 readerIteratorEntity->offset = offset;
98 return { env, objReaderIterator };
99 }
100
101 struct ReaderIteratorArg {
102 void *iterator = nullptr;
103 int64_t offset = 0;
104 };
105
AsyncExec(ReaderIteratorArg & readerIterator,const string & pathStr)106 static NError AsyncExec(ReaderIteratorArg &readerIterator, const string &pathStr)
107 {
108 readerIterator.iterator = ::ReaderIterator(pathStr.c_str());
109 if (readerIterator.iterator == nullptr) {
110 HILOGE("Failed to read lines of the file, error: %{public}d", errno);
111 return NError(errno);
112 }
113 int ret = GetFileSize(pathStr, readerIterator.offset);
114 if (ret < 0) {
115 HILOGE("Failed to get size of the file");
116 return NError(ret);
117 }
118
119 return NError(ERRNO_NOERR);
120 }
121
Async(napi_env env,napi_callback_info info)122 napi_value ReadLines::Async(napi_env env, napi_callback_info info)
123 {
124 NFuncArg funcArg(env, info);
125 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
126 HILOGE("Number of arguments unmatched");
127 NError(EINVAL).ThrowErr(env);
128 return nullptr;
129 }
130
131 auto [succPath, path, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
132 if (!succPath) {
133 HILOGE("Invalid path from JS first argument");
134 NError(EINVAL).ThrowErr(env);
135 return nullptr;
136 }
137
138 if (funcArg.GetArgc() >= NARG_CNT::TWO) {
139 int ret = CheckOptionArg(env, funcArg[NARG_POS::SECOND]);
140 if (ret) {
141 HILOGE("Invalid option.encoding parameter");
142 NError(ret).ThrowErr(env);
143 return nullptr;
144 }
145 }
146
147 auto arg = CreateSharedPtr<ReaderIteratorArg>();
148 if (arg == nullptr) {
149 HILOGE("Failed to request heap memory.");
150 NError(ENOMEM).ThrowErr(env);
151 return nullptr;
152 }
153 auto cbExec = [arg, pathStr = string(path.get())]() -> NError {
154 return AsyncExec(*arg, pathStr);
155 };
156
157 auto cbCompl = [arg](napi_env env, NError err) -> NVal {
158 if (err) {
159 return { env, err.GetNapiErr(env) };
160 }
161 return InstantiateReaderIterator(env, arg->iterator, arg->offset, true);
162 };
163
164 NVal thisVar(env, funcArg.GetThisVar());
165 if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
166 !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
167 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_READLINES_NAME, cbExec, cbCompl).val_;
168 } else {
169 NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
170 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_READLINES_NAME, cbExec, cbCompl).val_;
171 }
172 }
173
Sync(napi_env env,napi_callback_info info)174 napi_value ReadLines::Sync(napi_env env, napi_callback_info info)
175 {
176 NFuncArg funcArg(env, info);
177 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
178 HILOGE("Number of arguments unmatched");
179 NError(EINVAL).ThrowErr(env);
180 return nullptr;
181 }
182
183 auto [succPath, path, ignore] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
184 if (!succPath) {
185 HILOGE("Invalid path from JS first argument");
186 NError(EINVAL).ThrowErr(env);
187 return nullptr;
188 }
189
190 if (funcArg.GetArgc() == NARG_CNT::TWO) {
191 int ret = CheckOptionArg(env, funcArg[NARG_POS::SECOND]);
192 if (ret) {
193 HILOGE("Invalid option.encoding parameter");
194 NError(ret).ThrowErr(env);
195 return nullptr;
196 }
197 }
198
199 auto iterator = ::ReaderIterator(path.get());
200 if (iterator == nullptr) {
201 HILOGE("Failed to read lines of the file, error: %{public}d", errno);
202 NError(errno).ThrowErr(env);
203 return nullptr;
204 }
205
206 int64_t offset = 0;
207 int ret = GetFileSize(path.get(), offset);
208 if (ret != 0) {
209 HILOGE("Failed to get size of the file");
210 return nullptr;
211 }
212
213 return InstantiateReaderIterator(env, iterator, offset).val_;
214 }
215
216 } // ModuleFileIO
217 } // FileManagement
218 } // OHOS