Storage component¶
Implemented: Symbian::Storage exports ReadOnlyFile, WritableFile,
CreateDirectories and DirectoryReader in
<symbian/api/storage/storage.h>.
Motivation and modernization¶
The File Server APIs use RFs plus RFile or RDir, descriptor parameters,
integer errors and explicit Close() calls. Each public C++ object now owns
its session and subsession, is move-only, and closes both exactly once. A
std::u16string_view path preserves the native UTF-16 spelling without a
lossy conversion. absl::StatusOr distinguishes an empty file or directory
from an error, including PermissionDenied for protected paths.
Ownership and cost¶
ReadAt fills a caller-owned std::span<std::byte> directly. Reusing one
open file avoids reconnecting to the File Server and allocating a buffer on
every read. DirectoryReader::Next() yields one owned entry at a time, so a
large directory never becomes a large result vector. The legacy handles and
descriptors remain in native_storage.cc. Paths must be absolute UTF-16 drive
paths. File Server calls may block; create, use and destroy each owner on the
same worker thread while event callbacks remain short. Native session owners must remain on the creating worker thread.
WritableFile uses the same ownership rule and reads directly from a caller
std::span<const std::byte> during synchronous WriteAt; there is no
per-write vector or string allocation. WriteMode distinguishes creating a
new file, opening an existing file, and deliberately replacing a file. Parent
creation is an explicit CreateDirectories call. Flush exposes the File
Server's flush request and its error without claiming storage hardware
durability beyond that native contract. The open file uses exclusive sharing,
so concurrent writers cannot silently race through this owner. All operations
respect the File Server's data-cage and drive permissions.
FileCopy is pull-driven rather than a long opaque call. It owns a reusable
32 KiB buffer and at most one read and one write per Step(). Callers can
publish each CopyProgress immediately and decide whether to schedule another
step. Cancel() is an atomic request callable from another thread; the next
checkpoint returns Cancelled, with partial progress still inspectable on
the worker. Directory listings return one entry per Next(); their own
Cancel() stops the next entry at a checkpoint. In-flight synchronous File
Server calls cannot yet be preempted with a proven wall-clock bound; these
APIs promise a bounded amount of work and memory per step, not a fixed maximum
I/O latency.
This distinction matters when an underlying drive or server stops responding.
Read only a bounded prefix¶
For a preview or file signature, reuse caller storage instead of allocating the
whole file. ReadAt can return fewer bytes than requested:
#include <array>
#include <string>
#include "symbian/api/storage/storage.h"
absl::StatusOr<std::string> ReadPreview(std::u16string_view path) {
auto file = symbian::api::storage::ReadOnlyFile::Open(path);
if (!file.ok()) {
return file.status();
}
std::array<std::byte, 4096> buffer{};
auto count = file->ReadAt(0, buffer);
if (!count.ok()) {
return count.status();
}
return std::string(reinterpret_cast<const char*>(buffer.data()), *count);
}
The returned bytes are not implicitly decoded as text. A text preview must select an encoding and handle an incomplete final character.
Save a small draft¶
Link Symbian::Storage and run the whole function on one worker. Substitute
your application's secure ID for E0000830 in the private path. This explicitly
replaces the previous draft; it does not implement an atomic multi-file update.
#include <span>
#include <string_view>
#include "symbian/api/storage/storage.h"
absl::Status SaveDraft(std::string_view utf8) {
namespace files = symbian::api::storage;
if (utf8.size() > 32768) {
return absl::ResourceExhaustedError("Draft exceeds 32 KiB");
}
auto status = files::CreateDirectories(u"C:\\private\\E0000830\\");
if (!status.ok()) {
return status;
}
auto file = files::WritableFile::Open(u"C:\\private\\E0000830\\draft.txt",
files::WriteMode::kReplaceExisting);
if (!file.ok()) {
return file.status();
}
status = file->WriteAt(0, std::as_bytes(std::span(utf8.data(), utf8.size())));
return status.ok() ? file->Flush() : status;
}
The file and session close on return, including error paths. Flush reports the
File Server result; it cannot promise durability beyond the native service.
Restrictions¶
Offsets must be below 2 GiB. Large-file APIs and metadata/watch subscriptions
are unavailable. Cancellation is checked between synchronous operations;
in-flight I/O has no fixed cancellation or timeout bound.
WriteAt does not provide an atomic update across multiple calls or rollback
after a failed write.