diff --git a/scripts/grade-11.sh b/scripts/grade-11.sh new file mode 100755 index 0000000..8666182 --- /dev/null +++ b/scripts/grade-11.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -e +set -uo pipefail +IFS=$'\n\t' + +# Imports library. +BASEDIR=$(dirname "$0") +source $BASEDIR/grade-utils.sh + +RUNNERS=( + "cargo" + "cargo --release" + "cargo_asan" + "cargo_asan --release" + "cargo_tsan" + "cargo_tsan --release" +) + +# Lints. +run_linters || exit 1 + +# Executes test for each runner. +for RUNNER in "${RUNNERS[@]}"; do + echo "Running with $RUNNER..." + + TESTS=("--lib assignment11_grade") + if [ $(run_tests) -ne 0 ]; then + exit 1 + fi +done + +exit 0 diff --git a/scripts/prepare-submissions.sh b/scripts/prepare-submissions.sh index 3a8e550..35ee7ac 100755 --- a/scripts/prepare-submissions.sh +++ b/scripts/prepare-submissions.sh @@ -7,3 +7,4 @@ BASEDIR=$(dirname "$0")/.. mkdir -p $BASEDIR/target zip -rj $BASEDIR/target/assignment04.zip src/assignments/assignment04 +zip -rj $BASEDIR/target/assignment11.zip src/assignments/assignment11 diff --git a/src/assignments/assignment11/linked_list.rs b/src/assignments/assignment11/linked_list.rs new file mode 100644 index 0000000..5d772e3 --- /dev/null +++ b/src/assignments/assignment11/linked_list.rs @@ -0,0 +1,62 @@ +//! Singly linked list. +//! +//! Consult . + +use std::fmt::Debug; + +/// Node of the list. +#[derive(Debug)] +pub struct Node { + /// Value of current node. + pub value: T, + + /// Pointer to the next node. If it is `None`, there is no next node. + pub next: Option>>, +} + +impl Node { + /// Creates a new node. + pub fn new(value: T) -> Self { + Self { value, next: None } + } +} + +/// A singly-linked list. +#[derive(Debug)] +pub struct SinglyLinkedList { + /// Head node of the list. If it is `None`, the list is empty. + head: Option>, +} + +impl Default for SinglyLinkedList { + fn default() -> Self { + Self::new() + } +} + +impl SinglyLinkedList { + /// Creates a new list. + pub fn new() -> Self { + Self { head: None } + } + + /// Adds the given node to the front of the list. + pub fn push_front(&mut self, value: T) { + todo!() + } + + /// Adds the given node to the back of the list. + pub fn push_back(&mut self, value: T) { + todo!() + } + + /// Removes and returns the node at the front of the list. + pub fn pop_front(&mut self) -> Option { + todo!() + } + + /// Removes and returns the node at the back of the list. + pub fn pop_back(&mut self) -> Option { + todo!() + } +} diff --git a/src/assignments/assignment11/mock_storage.rs b/src/assignments/assignment11/mock_storage.rs new file mode 100644 index 0000000..a5d9604 --- /dev/null +++ b/src/assignments/assignment11/mock_storage.rs @@ -0,0 +1,97 @@ +//! Mock storage. +//! +//! Consult . + +use std::cell::RefCell; +use std::collections::HashMap; + +/// Mock storage. +#[derive(Debug)] +pub struct MockStorage { + /// Files stored in the storage. + /// + /// Each entry of the hashmap represents the `(name, size)` of the file. + files: RefCell>, + + /// Capacity of the storage. + /// + /// The total size of files stored on the storage cannot exceed the capacity. + capacity: usize, +} + +impl MockStorage { + /// Creates a new mock storage. + pub fn new(capacity: usize) -> Self { + Self { + files: RefCell::new(HashMap::new()), + capacity, + } + } +} + +/// Trait for storage object. +pub trait Storage { + /// Uploads a file. If a file with the same name already exists in the storage, overwrite it. + /// + /// Returns `Err` with insufficient memory size if there is no free space to upload a file. + fn upload(&self, name: &str, size: usize) -> Result<(), usize>; + + /// Returns the used memory size of the storage. + fn used(&self) -> usize; + + /// Returns the capacity of the storage. + fn capacity(&self) -> usize; +} + +impl Storage for MockStorage { + fn upload(&self, name: &str, size: usize) -> Result<(), usize> { + todo!() + } + + fn used(&self) -> usize { + todo!() + } + + fn capacity(&self) -> usize { + todo!() + } +} + +/// File uploader. +/// +/// It uploads files to the internal storage. +#[derive(Debug)] +pub struct FileUploader<'a, T: Storage> { + storage: &'a T, +} + +impl<'a, T: Storage> FileUploader<'a, T> { + /// Creates a new file uploader with given internal storage. + pub fn new(storage: &'a T) -> Self { + Self { storage } + } + + /// Uploads a file to the internal storage. + pub fn upload(&self, name: &str, size: usize) -> Result<(), usize> { + todo!() + } +} + +/// Storage usage analyzer. +#[derive(Debug)] +pub struct UsageAnalyzer<'a, T: Storage> { + storage: &'a T, + bound: f64, +} + +impl<'a, T: Storage> UsageAnalyzer<'a, T> { + /// Creates a new usage analyzer. + pub fn new(storage: &'a T, bound: f64) -> Self { + Self { storage, bound } + } + + /// Returns `true` if the usage of the internal storage is under the bound. + pub fn is_usage_under_bound(&self) -> bool { + todo!() + } +} diff --git a/src/assignments/assignment11/mod.rs b/src/assignments/assignment11/mod.rs new file mode 100644 index 0000000..64b4553 --- /dev/null +++ b/src/assignments/assignment11/mod.rs @@ -0,0 +1,9 @@ +//! Assignment 11: Familiarizing with smart pointers. +//! +//! You should fill out `todo!()` placeholders in such a way that `/scripts/grade-11.sh` works fine. +//! See `assignment11_grade.rs` and `/scripts/grade-11.sh` for the test script. +//! Run `/scripts/prepare-submissions.sh` and submit `/target/assignment11.zip` to . + +pub mod linked_list; +pub mod mock_storage; +pub mod tv_room; diff --git a/src/assignments/assignment11/tv_room.rs b/src/assignments/assignment11/tv_room.rs new file mode 100644 index 0000000..0315341 --- /dev/null +++ b/src/assignments/assignment11/tv_room.rs @@ -0,0 +1,108 @@ +//! TV Room Simulator. +//! +//! People can come to the TV room and watch TV. There are two types of TV watchers, manager and guest. +//! +//! The rule of the TV room is as follows: +//! +//! - Closed TV room can be opened by the manager. +//! - Guests can enter the TV room by the manager. +//! - Manager can leave the TV room earlier than guests. +//! - The TV room closes when the last person left the TV room. +//! +//! Both `Manager` and `Guest` have 'Rc' as a field, and its reference count indicates the number of people in +//! the TV room. When the 'Manager' and 'Guest' object is dropped, it means that the person leaves the TV room. +//! +//! Consult the following documentations: +//! - +//! - + +use std::{cell::RefCell, rc::Rc}; + +#[derive(Debug, Clone, Copy)] +enum TVRoomState { + Opened, + Closed, +} + +/// TV Room +#[derive(Debug)] +pub struct TVRoom { + /// Indicates whether the TV room is state. + state: RefCell, +} + +impl Default for TVRoom { + fn default() -> Self { + Self::new() + } +} + +impl TVRoom { + /// Creates a new TV room. + /// + /// Initial state of the TV room is closed. + pub fn new() -> Self { + Self { + state: RefCell::new(TVRoomState::Closed), + } + } + + /// Opens the TV room and returns the manager. + /// + /// Returns `None` if the TV room is already opened. + pub fn open(&self) -> Option> { + todo!() + } + + /// Returns whether the TV room is opened or not. + pub fn is_opened(&self) -> bool { + todo!() + } +} + +/// TV Room Manager. +/// +/// - The manager is special TV's watcher that has privileges to add other guests. +/// - If all watchers including the manager drop (~= leave the TV room), the TV must be turned off. +/// - Note that the manager can be dropped while other watchers are watching TV. +#[derive(Debug)] +pub struct Manager<'a> { + inner: Rc>, +} + +impl<'a> Manager<'a> { + fn new(tvstate: &'a RefCell) -> Self { + Self { + inner: Rc::new(Watcher::new(tvstate)), + } + } + + /// Adds new guest to the TV room. + pub fn new_guest(&self) -> Guest<'a> { + todo!() + } +} + +/// TV Room Guest. +#[derive(Debug)] +pub struct Guest<'a> { + inner: Rc>, +} + +#[derive(Debug)] +struct Watcher<'a> { + tvstate: &'a RefCell, +} + +impl<'a> Watcher<'a> { + fn new(tvstate: &'a RefCell) -> Self { + Self { tvstate } + } +} + +impl Drop for Watcher<'_> { + fn drop(&mut self) { + // When the last person leaves the TV room, the TV room should be closed. + todo!() + } +} diff --git a/src/assignments/assignment11_grade.rs b/src/assignments/assignment11_grade.rs new file mode 100644 index 0000000..94d2902 --- /dev/null +++ b/src/assignments/assignment11_grade.rs @@ -0,0 +1,81 @@ +#[cfg(test)] +mod test { + + #[test] + fn test_tv_room() { + use crate::assignments::assignment11::tv_room::*; + + let tv_room = TVRoom::new(); + assert!(!tv_room.is_opened()); + + // Turn on and add new guests. + let manager = tv_room.open().unwrap(); + assert!(tv_room.is_opened()); + let guest1 = manager.new_guest(); + let guest2 = manager.new_guest(); + drop(manager); + drop(guest1); + assert!(tv_room.open().is_none()); + drop(guest2); + assert!(!tv_room.is_opened()); + + // Turn on and add new guests. + let manager = tv_room.open().unwrap(); + assert!(tv_room.is_opened()); + let guest3 = manager.new_guest(); + drop(guest3); + assert!(tv_room.is_opened()); + drop(manager); + assert!(!tv_room.is_opened()); + } + + #[test] + fn test_mock_storage() { + use crate::assignments::assignment11::mock_storage::*; + + let mock_storage = MockStorage::new(100); + + let uploader1 = FileUploader::new(&mock_storage); + let uploader2 = FileUploader::new(&mock_storage); + + let usage_analyzer = UsageAnalyzer::new(&mock_storage, 0.75); + + assert!(uploader1.upload("file1.txt", 20).is_ok()); + assert!(usage_analyzer.is_usage_under_bound()); + + assert!(uploader2.upload("file2.txt", 30).is_ok()); + assert!(usage_analyzer.is_usage_under_bound()); + + assert!(uploader1.upload("file3.txt", 40).is_ok()); + assert!(!usage_analyzer.is_usage_under_bound()); + + assert_eq!(uploader2.upload("file4.txt", 50), Err(40)); + assert!(!usage_analyzer.is_usage_under_bound()); + + assert!(uploader1.upload("file3.txt", 10).is_ok()); + assert!(usage_analyzer.is_usage_under_bound()); + } + + #[derive(Debug, PartialEq, Eq)] + struct V(usize); + + #[test] + fn test_linked_list() { + use crate::assignments::assignment11::linked_list::*; + + let mut list = SinglyLinkedList::new(); + list.push_back(V(3)); + list.push_front(V(2)); + list.push_back(V(4)); + list.push_front(V(1)); + list.push_back(V(5)); + + assert_eq!(list.pop_front(), Some(V(1))); + assert_eq!(list.pop_back(), Some(V(5))); + assert_eq!(list.pop_front(), Some(V(2))); + assert_eq!(list.pop_back(), Some(V(4))); + assert_eq!(list.pop_front(), Some(V(3))); + assert_eq!(list.pop_back(), None); + assert_eq!(list.pop_front(), None); + } +} diff --git a/src/assignments/mod.rs b/src/assignments/mod.rs index a616d50..b69cbdb 100644 --- a/src/assignments/mod.rs +++ b/src/assignments/mod.rs @@ -24,3 +24,5 @@ pub mod assignment09; mod assignment09_grade; pub mod assignment10; mod assignment10_grade; +pub mod assignment11; +mod assignment11_grade;