andrewkdinh.com
Open in
urlscan Pro
97.84.73.48
Public Scan
Submitted URL: http://andrewkdinh.com/
Effective URL: https://andrewkdinh.com/
Submission: On March 31 via manual from US — Scanned from GB
Effective URL: https://andrewkdinh.com/
Submission: On March 31 via manual from US — Scanned from GB
Form analysis
0 forms found in the DOMText Content
Skip to main content ANDREW DINH * Work * Education * Projects * Competitions * Skills * Interests * Links * Résumé * LinkedIn * GitHub use std::fs::File; use std::io::{BufRead, BufReader}; use std::cmp::{min, max}; use std::path::Path; use super::piece_table::PieceTable; /// An editor window pub(crate) struct Editor { /// The piece table piece_table: PieceTable, /// Index we are currently at in `self.piece_table` (0-indexed) pt_index: usize, /// Path of file being editing (may not yet exist). /// Empty string if no file specified file_path: String, /// Reader of the file (Error if is an nonexistent file) reader: Result<BufReader<File>, String>, /// Whether we have read all of `self.reader` eof_reached: bool, /// Represents each line of the editor, and how many characters are in that line lines: Vec<usize>, /// Row cursor is at (1-indexed) row: usize, /// Column cursor is at (1-indexed) col: usize, /// Column cursor we want (1-indexed). When we move vertically, from a long /// line to short one, we want to try to get to a specific column col_want: usize, } impl Editor { /// Initialize a new editor from a file path (read a single line) pub(crate) fn new(file_path: String) -> Editor { // let (reader, eof_reached) = create_reader(file_path); let reader; let eof_reached; if file_path == "" { reader = Err("No file specified".to_string()); eof_reached = true; } else if Path::new(&file_path).is_file() { reader = Ok(BufReader::new(File::open(file_path.clone()).unwrap())); eof_reached = false; } else if Path::new(&file_path).is_dir() || file_path.ends_with("/") { panic!("No support (yet) for writing to directories"); } else { reader = Err("File doesn't exist".to_string()); eof_reached = true; } let mut editor = Editor {piece_table: PieceTable::new(), pt_index: 0, file_path: file_path, reader: reader, eof_reached: eof_reached, lines: Vec::new(), row: 1, col: 1, col_want: 1, }; if editor.read_lines(1) == 0 { editor.lines.push(0); } else { editor.lines.push(editor.piece_table.text_len()); } editor } /// Returns visible text pub(crate) fn text(&mut self) -> &str { self.piece_table.text() } /// Returns file path pub(crate) fn file_path(&self) -> &str { self.file_path.as_str() } /// Update the file path pub(crate) fn update_file_path(&mut self, file_path: String) { self.file_path = file_path; } /// Returns the current row pub(crate) fn row(&self) -> usize { self.row } /// Returns the current column pub(crate) fn col(&self) -> usize { self.col } /// Returns the number of columns in the specified `row` (1-indexed) pub(crate) fn num_cols(&self, row: usize) -> usize { *self.lines.get(row - 1).unwrap() } /// Returns the number of lines pub(crate) fn num_lines(&self) -> usize { self.lines.len() } /// Returns the length of the line (1-indexed) pub(crate) fn line_len(&self, line: usize) -> usize { *self.lines.get(line - 1).unwrap() } /// Returns whether the text of the file matches the text of `self.piece_table` pub(crate) fn text_matches(&self) -> bool { !self.piece_table.actions_taken() } /// Returns visible text from line `first` (inclusive) to `last` (exclusive) pub(crate) fn text_lines(&mut self, first: usize, last: usize) -> &str { if first >= last { panic!("First row ({}) must be less last ({})", first, last); } let mut start_index = 0; let mut end_index = 0; for (i, line) in self.lines.iter().enumerate() { if i < first - 1 { start_index += line + 1; end_index = start_index; } else if i >= last - 1 { break } else { end_index += line + 1; } } self.piece_table.text().get(start_index..end_index - 1).unwrap() } /// Returns the visible text for a single row pub(crate) fn text_line(&mut self, line: usize) -> &str { self.text_lines(line, line + 1) } /// Adds `text` at the current cursor position pub(crate) fn add_text(&mut self, text: String) { let mut from_end = 0; let mut num_lines = 0; let text_len = text.len(); let mut last_line_len = 0; for (i, line) in text.split('\n').enumerate() { if i == 0 { let curr_line_len = self.lines.get_mut(self.row - 1).unwrap(); from_end = *curr_line_len + 1 - self.col; if text.contains('\n') { *curr_line_len -= from_end; } *curr_line_len += line.len(); } else if self.row + i >= self.lines.len() { self.lines.push(line.len() + from_end); from_end = 0; } else { self.lines.insert(self.row + i, line.len() + from_end); from_end = 0; } num_lines += 1; last_line_len = line.len(); } self.piece_table.add_text(text, self.pt_index); if num_lines == 1 { self.right(text_len); } else { self.down(num_lines - 1); self.goto_col(last_line_len + 1); } } /// Deletes from current cursor position to (row, col) which are 1-indexed pub(crate) fn delete_text(&mut self, row: usize, col: usize) -> Result<(), String> { if row == self.row { if row == self.row && col == self.col { return Ok(()) // return Err("No text to delete".to_string()); } let line_len = self.lines.get_mut(row - 1).unwrap(); let first_col = min(self.col, col); let last_col = if first_col == self.col {col} else {self.col}; if last_col > *line_len + 1 { // panic!("Can't delete from {} to {} of line length {}", first_col, last_col, *line_len); return Err(format!("Can't delete from {} to {} of line length {}", first_col, last_col, *line_len)) } let len = last_col - first_col; *(line_len) -= len; if first_col == self.col { self.piece_table.delete_text(self.pt_index, self.pt_index + len); } else { self.piece_table.delete_text(self.pt_index - len, self.pt_index); self.col -= len; self.col_want = self.col; } return Ok(()) } let mut size = 0; let first_row = min(self.row, row); let first_col = if first_row == self.row {self.col} else {col}; let last_row = if first_row == self.row {row} else {self.row}; let last_col = if first_row == self.row {col} else {self.col}; if last_row == usize::MAX { // TODO: Don't actually read to end of file. Just pretend you did // If you do this, you have to update undo and redo to update self.eof_reached self.read_to_eof() } else { self.read_lines(max(self.lines.len(), row) - min(self.lines.len(), row)); } let first_line_len = self.lines.get_mut(first_row - 1).unwrap(); if first_col > *first_line_len + 1 { panic!("Invalid beginning column {} for row {}", first_col, first_row); } size += *first_line_len + 1 - (first_col - 1); *first_line_len -= *first_line_len - (first_col - 1); let first_line_len_copy = *first_line_len; let to_last_row = last_row == self.lines.len(); for _ in first_row + 1..last_row { let line_len = self.lines.remove(first_row); size += line_len + 1; } let last_line_len = self.lines.get_mut(last_row - (last_row - first_row) - 1).unwrap(); if last_col - 1 > *last_line_len { panic!("Invalid ending column {} for row {}", last_col, last_row); } *(last_line_len) -= last_col - 1; size += last_col - 1; if to_last_row { self.lines.pop(); } if first_row == self.row { self.piece_table.delete_text(self.pt_index, self.pt_index + size); } else { self.piece_table.delete_text(self.pt_index - size, self.pt_index); self.row = first_row; self.col = first_line_len_copy; self.col_want = self.col; } Ok(()) } /// Delete all text pub(crate) fn delete_all(&mut self) { if self.piece_table.text_len() == 0 { return } self.piece_table.delete_text(0, self.piece_table.text_len()); self.row = 1; self.col = 1; self.col_want = 1; self.pt_index = 0; } pub(crate) fn delete_to_end(&mut self) { self.delete_text(usize::MAX, usize::MAX).unwrap(); } /// Read `num_lines` from `reader`, updating `self.piece_table` & `self.lines` /// Returns number of lines actually read fn read_lines(&mut self, num_lines: usize) -> usize { if num_lines == 0 || self.eof_reached { return 0; } let mut lines_read = 0; let reader = self.reader.as_mut().unwrap(); for _ in 0..num_lines { let mut temp_str = String::new(); match reader.read_line(&mut temp_str) { Ok(0) => { self.eof_reached = true; break }, Ok(len) => { lines_read += 1; self.lines.push(len); self.piece_table.update_original_buffer(temp_str); }, Err(e) => panic!("Error reading file: {:?}", e), } } lines_read } /// Read to EOF, updating `self.piece_table` & `self.lines` fn read_to_eof(&mut self) { if self.eof_reached { return; } let reader = self.reader.as_mut().unwrap(); loop { let mut temp_str = String::new(); match reader.read_line(&mut temp_str) { Ok(0) => { self.eof_reached = true; break }, Ok(len) => { self.lines.push(len); self.piece_table.update_original_buffer(temp_str); }, Err(e) => panic!("Error reading file: {:?}", e), } } } /// Move the cursor up `num` places /// If unable to go up all the way, go to first row pub(crate) fn up(&mut self, num: usize) { if num == 0 || self.row == 1 { return } else if num >= self.row { self.up(self.row - 1); return } self.pt_index -= self.col; for i in 1..num { self.pt_index -= self.lines.get(self.row - i - 1).unwrap() + 1; } self.row -= num; let line_cols = self.lines.get(self.row - 1).unwrap(); self.col = min(self.col_want, line_cols + 1); self.pt_index -= line_cols + 1 - self.col; } /// Move the cursor down `num` places. /// If unable to go all the way down, go to last row pub(crate) fn down(&mut self, num: usize) { if num == 0 { return } else if self.row + num > self.lines.len() { let lines_read = self.read_lines(self.row + num - self.lines.len()); self.down(lines_read); return } self.pt_index += self.lines.get(self.row - 1).unwrap() + 1 - self.col + 1; for i in 1..num { self.pt_index += self.lines.get(self.row + i - 1).unwrap() + 1; } self.row += num; self.col = min(self.col_want, self.lines.get(self.row - 1).unwrap() + 1); self.pt_index += self.col - 1; } /// Move the cursor right `num` places. /// If unable to go all the way right, go to last column pub(crate) fn right(&mut self, num: usize) { let line_len = self.lines.get(self.row - 1).unwrap(); if num == 0 || self.col == line_len + 1 { return } else if self.col + num > line_len + 1 { self.goto_last_col(); return } self.col += num; self.pt_index += num; self.col_want = self.col; } /// Move the cursor left `num` places. /// If unable to go all the way left, go to first column pub(crate) fn left(&mut self, num: usize) { if num == 0 { return } else if num >= self.col { self.left(self.col - 1); return } self.col -= num; self.pt_index -= num; self.col_want = self.col; } /// Move to a certain column in the current row pub(crate) fn goto_col(&mut self, col: usize) { if col > *self.lines.get(self.row - 1).unwrap() + 1 { self.goto_last_col(); } else if self.col == col { self.col_want = col; } else if col < self.col { self.left(self.col - col) } else { self.right(col - self.col) } } /// Move to a certain row pub(crate) fn goto_row(&mut self, row: usize) { if self.row == row { return } else if row < self.row { self.up(self.row - row) } else { self.down(row - self.row) } } /// Move to (closest) `row` and `col` pub(crate) fn goto(&mut self, row: usize, col: usize) { self.goto_row(row); self.goto_col(col); } /// Move to the last column in the current row pub(crate) fn goto_last_col(&mut self) { self.goto_col(*self.lines.get(self.row - 1).unwrap() + 1) } /// Move to the last row pub(crate) fn goto_last_row(&mut self) { self.read_to_eof(); self.goto(self.lines.len(), 1) } } #[cfg(test)] mod tests { use super::*; #[test] fn add_text() { let mut editor = Editor::new(String::new()); let mut want_str = "hello"; editor.add_text(want_str.to_string()); assert_eq!(editor.text(), want_str); editor = Editor::new(String::new()); want_str = "hello\nbye"; editor.add_text(want_str.to_string()); assert_eq!(editor.text(), want_str); } #[test] fn delete_text() { let mut editor = Editor::new(String::new()); let mut want_str = ""; editor.add_text("abcd".to_string()); editor.delete_text(1, 1).unwrap(); assert_eq!(editor.text(), want_str); editor = Editor::new(String::new()); want_str = "ab\nef"; editor.add_text("ab\ncd\nef".to_string()); editor.goto(2, 1); editor.delete_text(3, 1).unwrap(); assert_eq!(editor.text(), want_str); assert_eq!(editor.num_lines(), want_str.lines().count()); } #[test] fn movement() { let mut editor = Editor::new(String::new()); let mut want_str = "hello\nbye"; editor.add_text("hello\n".to_string()); editor.down(1); editor.add_text("bye".to_string()); assert_eq!(editor.text(), want_str); editor = Editor::new(String::new()); want_str = "hello\nbye"; editor.add_text("h\n".to_string()); editor.down(1); editor.add_text("bye".to_string()); editor.up(1); editor.add_text("ello".to_string()); assert_eq!(editor.text(), want_str); } #[test] fn edge_cases() { let mut editor = Editor::new(String::new()); let mut want_str = "hell"; editor.add_text("hello\n\n".to_string()); editor.delete_text(1, 5).unwrap(); assert_eq!(editor.text(), want_str); editor = Editor::new(String::new()); want_str = "hello"; editor.add_text("hello\n\n".to_string()); editor.delete_text(1, 6).unwrap(); assert_eq!(editor.text(), want_str); } #[test] fn more_cases() { let mut editor = Editor::new(String::new()); let mut want_str = "hello\nbye"; editor.add_text("hello\n\n".to_string()); editor.add_text("bye".to_string()); editor.up(2); editor.goto_last_col(); editor.delete_text(editor.row() + 1, 1).unwrap(); assert_eq!(editor.text(), want_str); println!("{:?}", editor.lines); assert_eq!(editor.num_lines(), want_str.lines().count()); editor = Editor::new(String::new()); want_str = "hellobye"; editor.add_text("hello\n\n".to_string()); editor.add_text("bye".to_string()); editor.up(2); editor.goto_last_col(); editor.delete_text(editor.row() + 2, 1).unwrap(); assert_eq!(editor.text(), want_str); assert_eq!(editor.num_lines(), want_str.lines().count()); } #[test] fn text_lines() { let mut editor = Editor::new(String::new()); let mut want_str = "abc"; editor.add_text("abc".to_string()); assert_eq!(editor.text_lines(1, 2), want_str); editor = Editor::new(String::new()); want_str = "abc"; editor.add_text("abc\n\ncd".to_string()); assert_eq!(editor.text_line(1), want_str); } } ANDREW K DINH -------------------------------------------------------------------------------- SOFTWARE ENGINEER Résumé LinkedIn Mastodon Email Music Dino img are lazy-loaded and will not be displayed without enabling Javascript andrewkdinh.com github.com/andrewkdinh linkedin.com/in/andrewkdinh-com WORK EXPERIENCE -------------------------------------------------------------------------------- APPLE Software Engineer - Proactive Intelligence FEB 2022 - OCT 2023 * Worked on a project to generate a private knowledge graph from on-device data to provide personalized suggestions to users * Designed a highly-performant system that could save its progress and continue at a later time in order to keep battery usage to a minimum * Reduced CPU and memory usage by 15% by analyzing stack traces and memory graphs * Added diagnostics and client-side metrics to triage issues affecting power users Homepage APPLE Software Engineer Intern - Ad Platforms MAY 2021 - AUG 2021 * Implemented features in backend infrastructure to assist the sales team with keeping track of employee compensation and client onboarding status * Collaborated with partner teams to coordinate data scheme changes so their data analytics pipelines could correctly consume and interpret upstream updates * Worked closely with QA to validate backwards compatibility and ensure a smooth upgrade Search Ads SUMUP ANALYTICS Software Engineer DEC 2019 - OCT 2020 * Performed text analysis on search engine results to categorize and extract relevant keywords * Designed a browser extension and corresponding REST API to display results in webpages * Created Jupyter notebooks to visualize and compare performance of various NLP models Nucleus SDK RUSH ORDER Full Stack Engineer JUL 2019 - JAN 2020 * Improved page load times by 25% by optimizing inefficient JavaScript and SQL queries * Redesigned the internal website to be mobile-responsive, including a mobile navigation menu Homepage EDUCATION -------------------------------------------------------------------------------- UNIVERSITY OF CALIFORNIA, BERKELEY B.A in Computer Science AUG 2019 - DEC 2021 Data Structures, Efficient Algorithms, Operating Systems, Computer Security, Database Systems, Information Devices and Systems, Programming Languages and Compilers, Artificial Intelligence GAVILAN COLLEGE A.S in Computer Programming | A.A in Natural Science AUG 2015 - AUG 2019 C++, Python, UNIX/Linux, Java, Assembly Language, Discrete Structures, Webpage Authoring, Human Anatomy & Physiology PROJECTS -------------------------------------------------------------------------------- SYSTEM ADMINISTRATION Self-hosted network services and web applications Docker, Linux, Nginx, Apache * Self-hosts web applications and network services using multiple Linux servers * Deployed Nextcloud to sync files, calendar, and reminders between mobile and desktop devices * Utilized Docker to easily containerize and upgrade applications while using Nginx as a reverse proxy to serve each on its own subdomain * Uses Nginx to reverse proxy services and serve this website Dashboard Status Page Analytics Previous Next VIA An efficient text editor written in Rust Rust, CLI, piece table * Inspired by Vim, supporting its keybindings and basic commands * Ability to use a mouse to move the cursor and select text * Implemented a piece table data structure for efficiently inserting text * Designed from the start to handle text files of arbitrarily large sizes by only loading what fits in the viewport GitHub Previous Next DEATH CODE Share your secrets after death Python, Flask, Docker * Utilizes Shamir's Secret Sharing scheme to safely share secrets with others * After splitting a secret among a group of people, the secret can only be reconstructed once a sufficient number of people combine their parts together, presumably only after you are gone from this earth * Has a CAPTCHA for preventing brute-force attempts Demo GitHub Previous Next FUND INDICATORS Python application to find indicators of mutual fund performance Python, Beautiful Soup, NumPy, qualitative analysis * Allows investors to easily find relationships between various attributes of mutual funds and previous performance * Designed to be extremely reliable and error-tolerant by using multiple data sources and caching requests * Based off the research detailed in Performance Indicators of Mutual Funds, this program uses NumPy to analyze results and calculate key values to make predictions GitHub Previous Next COMPETITIONS -------------------------------------------------------------------------------- ALLIUM HACKS Coding competition Gilroy | 1st Place * Web application that allowed users to have real-time, collaborative conversations with an artificial intelligence implementation named Jade * WebSockets and Flask were used to create a seamless conversation with Jade AI Jade AI CYBERPATRIOT Cybersecurity competition San Jose | 3rd place * Worked in a mock scenario to upgrade the infrastructure of a company that had recently suffered a data breach * Implemented best security and operational practices while maintaining the specific company settings, software and data intact Homepage News CAL HACKS 6.0 World’s largest collegiate hackathon UC Berkeley * Created AJA Messenger, an Android app which filters spam using machine learning * Collaborated with companies like Google and Facebook, utilizing their existing technologies to create our own mobile application Homepage Product CALIFORNIA CYBER INNOVATION CHALLENGE Cybersecurity competition Cal Poly, San Luis Obispo * Collected and analyzed a combination of digital and physical evidence in order to stop a healthcare themed cyber plot * Utilized state-of-the-art forensive tools including Wireshark and Burp Suite to extract and collect digital evidence * Used a combination of technical, analytical and persuasive skills to "prove our case" to a panel of judges Homepage SKILLS -------------------------------------------------------------------------------- LANGUAGES * Python * Java * C * Go * Rust * OCaml SOFTWARE * Docker * Linux * Nginx * Apache WEB DEVELOPMENT * HTML * JavaScript * jQuery * Bootstrap * SQL OTHER * Browser extensions * LaTeX * Google Suite * Microsoft Office * Cybersecurity INTERESTS -------------------------------------------------------------------------------- VOLUNTEERING OCF | Circle K | JPOG Helping the community, one step at a time * OCF: Student organization dedicated to free computing for all UC Berkeley students, faculty, and staff * Circle K: Student-led organization dedicated to service, leadership, and fellowship * JPOG: A free, weekly tutoring program teaching subjects including Spanish, piano, and computer programming OCF Circle K PRIVACY Your data is yours alone Defend against surveillance * Taught a class at UC Berkeley about digital privacy * Advises others in securing their online identities from nefarious actors * Strong proponent of freedom of speech as well as against online censorship * Supports open source projects and privacy-conscious services Digital Privacy DeCal PrivacyTools OPEN SOURCE SOFTWARE Contributor Git * Donates to open source projects on a monthly basis * Contributes to free and open source software * Proponent of copy-left licenses such as GPL Open Collective Open Source Initiative PHOTOGRAPHY Amateur photographer Lightroom, Photoshop, GIMP * Experienced with landscape and portrait photography * Takes picture using a Canon EOS Rebel T3 DSLR camera Portfolio DINO GAME ← geekring → ← Hotline Webring → ← Retronaut Webring → ← 🕸💍 – An IndieWeb Webring → ← Fediring → © 2024 Andrew Dinh Contact Form Licenses Andrew Dinh Andrew K. Dinh andrewkdinh Male Software Engineer Software engineer developing a better feature. My interests include photography, privacy, & open source software. California United States Open Source Software Privacy Web Design Self-hosted Docker Programming Photography