Cloud Storage System, Part 4: Backup and Restore
Problem statement
Your task is to implement a simple cloud storage system. All operations that should be supported are listed below.
Solving this task consists of several levels. Subsequent levels are opened when the current level is correctly solved. You always have access to the data for the current and all previous levels.
Requirements
Your task is to implement a simple cloud storage system that maps objects (files) to their metainformation. Specifically, the storage should maintain files and information about them, including each file name and size. This system is in-memory; it does not use the real filesystem.
The levels are cumulative:
- Level 1 supports adding, retrieving, and deleting files.
- Level 2 displays the largest files matching a prefix.
- Level 3 adds capacity-limited users and user merging.
- Level 4 backs up and restores a user's files.
To move to the next level, all tests at the current level must pass.
Note
The queries never call operations that produce a collision between a file name and a directory name.
FastPrep Operation-Sequence Adapter
FastPrep calls the function once with operations. Process the rows from left to right while preserving one shared storage state. Every row starts with an uppercase operation name followed by that method's string arguments.
Return one string-array row for every input operation:
- Encode a Boolean as
["true"]or["false"]. - Encode an integer as one element, such as
["10"]. - Encode
Noneas an empty row[]. - Return the formatted list from
GET_N_LARGESTdirectly; an empty list is also[].
Multipart Series
Level 1: File Operations
The cloud storage system should support file manipulation.
add_file(self, name: str, size: int) -> booladds a new filenameto the storage.sizeis the amount of memory required in bytes. The operation fails if a file with the samenamealready exists. ReturnTrueif the file was added successfully orFalseotherwise. The adapter row is["ADD_FILE", name, size]. Files added this way are owned by the unlimitedadminuser.get_file_size(self, name: str) -> int | Nonereturns the size of filenameif it exists, orNoneotherwise. The adapter row is["GET_FILE_SIZE", name].delete_file(self, name: str) -> int | Nonedeletes filename. Return the deleted file size when deletion succeeds, orNoneif the file does not exist. The adapter row is["DELETE_FILE", name].
Level 2: Largest Files
Implement an operation for retrieving statistics about files with a specific prefix.
get_n_largest(self, prefix: str, n: int) -> list[str]returns the names of the topnlargest files whose names start withprefix, formatted as["<name_1>(<size_1>)", ..., "<name_n>(<size_n>)"].- Sort matching files by size in descending order. Break a size tie by file name in lexicographical ascending order.
- If there are no matching files, return an empty list. If fewer than
nfiles match, return all of them in the specified format. - The adapter row is
["GET_N_LARGEST", prefix, n].
The visible source table contains one malformed no-match call whose second argument is a file name even though the declared signature requires n: int. The judged contract follows the declared signature: n is an integer, and a prefix with no matches returns an empty row.
Level 3: Users and Capacity
Support queries from different users. All users share one common filesystem, and every non-admin user has a storage-capacity limit.
add_user(self, user_id: str, capacity: int) -> booladds a new user withcapacitybytes. The total size of files owned byuser_idcannot exceed this limit. The operation fails if the user already exists. ReturnTrueon success andFalseotherwise. The adapter row is["ADD_USER", user_id, capacity].add_file_by(self, user_id: str, name: str, size: int) -> int | Nonebehaves likeadd_file, but the new file is owned byuser_id. The operation fails when the user is absent, the name is already occupied, or the addition would exceed the user's capacity. Return the user's remaining capacity after success, orNoneotherwise. The adapter row is["ADD_FILE_BY", user_id, name, size].- Every
ADD_FILEoperation from Level 1 is run by theadminuser, who has unlimited storage capacity. merge_user(self, user_id_1: str, user_id_2: str) -> int | Nonemergesuser_id_2intouser_id_1. Transfer ownership of every file owned byuser_id_2, add the remaining storage capacity ofuser_id_2touser_id_1's limit, and deleteuser_id_2. Returnuser_id_1's remaining capacity, orNonewhen either user is absent or the IDs are equal. Neither merge argument isadmin. The adapter row is["MERGE_USER", user_id_1, user_id_2].
Level 4: Backup and Restore
Allow users to back up their files.
backup_user(self, user_id: str) -> int | Nonebacks up the current state of every file owned byuser_id, including each file name and size. The backup is stored separately and is not changed by later file-manipulation queries. A new backup replaces any previous backup for the same user. Return the number of backed-up files, orNoneifuser_iddoes not exist. The adapter row is["BACKUP_USER", user_id].restore_user(self, user_id: str) -> int | Nonerestores the user's files to the latest backup. If there is no backup, delete all files currently owned by the user. When a backed-up file name is currently occupied by another user, ignore that file. Return the number of files restored successfully, orNoneifuser_iddoes not exist. The adapter row is["RESTORE_USER", user_id].merge_userdoes not changeuser_id_1's backup, anduser_id_2is deleted together with its backup.restore_userdoes not change the user's capacity.
Function
cloudStorageSystem(operations: String[][]) → String[][]Examples
Example 1
operations = [["ADD_FILE","/dir1/dir2/file.txt","10"],["ADD_FILE","/dir1/dir2/file.txt","5"],["GET_FILE_SIZE","/dir1/dir2/file.txt"],["DELETE_FILE","/non-existing.file"],["DELETE_FILE","/dir1/dir2/file.txt"],["GET_FILE_SIZE","/not-existing.file"]]return = [["true"],["false"],["10"],[],["10"],[]]This preserves every Level 1 row shown in the source example: the first add succeeds, the duplicate add fails, the existing size is 10, deleting a missing file returns None, deleting the existing file returns 10, and the final lookup returns None.
Example 2
operations = [["ADD_FILE","/dir/file1.txt","5"],["ADD_FILE","/dir/file2","20"],["ADD_FILE","/dir/deeper/file3.mov","9"],["GET_N_LARGEST","/dir","2"],["GET_N_LARGEST","/dir/file","3"],["GET_N_LARGEST","/another_dir","3"],["ADD_FILE","/big_file.mp4","20"],["GET_N_LARGEST","/","2"]]return = [["true"],["true"],["true"],["/dir/file2(20)","/dir/deeper/file3.mov(9)"],["/dir/file2(20)","/dir/file1.txt(5)"],[],["true"],["/big_file.mp4(20)","/dir/file2(20)"]]The first three files reproduce the source's Level 2 table. Prefix filtering keeps only matching names, size determines primary order, and the final root-prefix query resolves the size-20 tie lexicographically. The no-match row follows the explicit empty-list rule.
Example 3
operations = [["ADD_USER","user1","100"],["ADD_USER","user2","40"],["ADD_FILE_BY","user1","/a","60"],["ADD_FILE_BY","user2","/b","10"],["ADD_FILE_BY","user2","/c","30"],["MERGE_USER","user1","user2"],["GET_FILE_SIZE","/b"],["ADD_FILE_BY","user2","/d","1"],["ADD_FILE_BY","user1","/d","40"],["MERGE_USER","user1","user1"]]return = [["true"],["true"],["40"],["30"],["0"],["40"],["10"],[],["0"],[]]Before the merge, user1 has 40 bytes remaining and user2 has none. The merge transfers both files, deletes user2, and leaves user1 with 40 bytes. Calls for the deleted user and a self-merge return None.
Example 4
operations = [["ADD_USER","user","100"],["ADD_FILE_BY","user","/file3.mp4","60"],["ADD_FILE_BY","user","/file4.txt","10"],["BACKUP_USER","user"],["DELETE_FILE","/file3.mp4"],["DELETE_FILE","/file4.txt"],["ADD_FILE","/file3.mp4","140"],["ADD_FILE_BY","user","/dir/file5.new","20"],["RESTORE_USER","user"],["GET_FILE_SIZE","/file3.mp4"],["GET_FILE_SIZE","/file4.txt"],["GET_FILE_SIZE","/dir/file5.new"]]return = [["true"],["40"],["30"],["2"],["60"],["10"],["true"],["80"],["1"],["140"],["10"],[]]The backup remembers /file3.mp4 and /file4.txt. Before restore, admin occupies the former name and the user owns /dir/file5.new. Restore deletes the user's current file, skips the conflicting name, restores /file4.txt, and returns 1.
Constraints
1 <= operations.length <= 2000.- Every operation row is well-formed and uses one of the operation names described above.
- Sizes, capacities, and
nare positive base-10 integers at most10^9. - File names, prefixes, and user IDs are non-empty case-sensitive ASCII strings of length at most
100. - The input does not create collisions between file and directory names.
- All users share one file-name namespace;
adminexists initially and has unlimited capacity. - Process operations in the supplied order. The system is empty initially except for
admin.