diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/cursor.cpp | 13 | ||||
-rw-r--r-- | src/client/window.cpp | 16 | ||||
-rw-r--r-- | src/common/ipc.cpp | 40 | ||||
-rw-r--r-- | src/jet.cpp | 72 | ||||
-rw-r--r-- | src/jetc.cpp | 98 | ||||
-rw-r--r-- | src/server/buffer.cpp | 45 | ||||
-rw-r--r-- | src/server/client.cpp | 93 | ||||
-rw-r--r-- | src/server/page.cpp | 87 | ||||
-rw-r--r-- | src/server/point.cpp | 139 |
9 files changed, 603 insertions, 0 deletions
diff --git a/src/client/cursor.cpp b/src/client/cursor.cpp new file mode 100644 index 0000000..4e76151 --- /dev/null +++ b/src/client/cursor.cpp @@ -0,0 +1,13 @@ +struct Cursor { + int x; + int y; + Window *w; + + void move_left() { + if (x > 0) x--; + } + + void move_right() { + if (x < w->width - 1 && w->pos(x + 1, y) != 0) x++; + } +}; diff --git a/src/client/window.cpp b/src/client/window.cpp new file mode 100644 index 0000000..e718e63 --- /dev/null +++ b/src/client/window.cpp @@ -0,0 +1,16 @@ +struct Window { + char *view; + int width; + int height; + + void init() { + view = new char[width * height]; + for (int i = 0; i < width * height; i++) { + view[i] = 0; + } + } + + char pos(size_t x, size_t y) { + return view[x + y * width]; + } +}; diff --git a/src/common/ipc.cpp b/src/common/ipc.cpp new file mode 100644 index 0000000..7d46307 --- /dev/null +++ b/src/common/ipc.cpp @@ -0,0 +1,40 @@ +enum Operation { + OP_NULL = 0, + OP_MOVE1, + OP_MOVE2, + OP_MOVE4, + OP_MOVE8, + OP_INSERT, + OP_DELETE, + OP_SHOW +}; + +void encode2(int16_t data, int8_t *message, size_t offset) { + message[offset] = data & 0x00ff; + message[offset + 1] = (data & 0xff00) >> 8; +} + +int16_t decode2(int8_t *message, size_t offset) { + return (int16_t)message[offset + 1] << 8 & 0xff00 + | (int16_t)message[offset] & 0x00ff; +} + +void encode4(int32_t data, int8_t *message, size_t offset) { + encode2( data & 0x0000ffff, message, offset); + encode2((data & 0xffff0000) >> 16, message, offset + 2); +} + +int32_t decode4(int8_t *message, size_t offset) { + return (int32_t)decode2(message, offset + 2) << 16 & 0xffff0000 + | (int32_t)decode2(message, offset) & 0x0000ffff; +} + +void encode8(int64_t data, int8_t *message, size_t offset) { + encode4( data & 0x00000000ffffffff, message, offset); + encode4((data & 0xffffffff00000000) >> 32, message, offset + 4); +} + +int64_t decode8(int8_t *message, size_t offset) { + return (int64_t)decode4(message, offset + 4) << 32 & 0xffffffff00000000 + | (int64_t)decode4(message, offset) & 0x00000000ffffffff; +} diff --git a/src/jet.cpp b/src/jet.cpp new file mode 100644 index 0000000..6cc4470 --- /dev/null +++ b/src/jet.cpp @@ -0,0 +1,72 @@ +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <sys/epoll.h> + +#include "common/ipc.cpp" +#include "server/page.cpp" +#include "server/buffer.cpp" +#include "server/point.cpp" +#include "server/client.cpp" + +#define PORT 6969 +#define MAX_EVENTS 10 + +int create_listener() { + int s = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in addr = { AF_INET, htons(PORT), htonl(INADDR_LOOPBACK)}; + int opt = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); + bind(s, (sockaddr *) &addr, sizeof(sockaddr_in)); + listen(s, MAX_EVENTS); + return s; +} + +int main() { + Buffer scratch("scratch"); + scratch.read_file("test.txt"); + + int listener = create_listener(); + + int epollfd = epoll_create1(0); + + epoll_event ev; + ev.events = EPOLLIN; + ev.data.fd = listener; + epoll_ctl(epollfd, EPOLL_CTL_ADD, listener, &ev); + + epoll_event events[MAX_EVENTS]; + Client *clients[1024] = {}; + + while (true) { + int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); + + for (int i = 0; i < nfds; i++) { + if (events[i].data.fd == listener) { + int clientfd = accept(listener, 0, 0); + ev.events = EPOLLIN | EPOLLET; + ev.data.fd = clientfd; + if (clients[clientfd]) { + delete clients[clientfd]; + } + Client *c = new Client(scratch); + c->sockfd = clientfd; + clients[clientfd] = c; + epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev); + } else { + clients[events[i].data.fd]->parse_message(); + } + } + } + + close(listener); +} diff --git a/src/jetc.cpp b/src/jetc.cpp new file mode 100644 index 0000000..9650eb6 --- /dev/null +++ b/src/jetc.cpp @@ -0,0 +1,98 @@ +#include <stdio.h> +#include <string.h> +#include <cursesw.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <unistd.h> + +#include "common/ipc.cpp" +#include "client/window.cpp" +#include "client/cursor.cpp" + +#define NORMAL_MODE 0 +#define INSERT_MODE 1 +#define PORT 6969 + +int main(int argc, char *argv[]) { + initscr(); + cbreak(); + noecho(); + intrflush(stdscr, FALSE); + keypad(stdscr, TRUE); + + Window window; + getmaxyx(stdscr, window.height, window.width); + window.init(); + + int s = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in addr = { AF_INET, htons(PORT), htonl(INADDR_LOOPBACK) }; + connect(s, (sockaddr *) &addr, sizeof(sockaddr_in)); + + int mode = NORMAL_MODE; + Cursor cursor = { 0, 0, &window }; + + int quit = 0; + while (!quit) { + clear(); + + int8_t msg[5]; + msg[0] = OP_SHOW; + encode2(window.width, msg, 1); + encode2(window.height, msg, 3); + write(s, msg, 5); + read(s, window.view, window.width * window.height); + for (int i = 0; i < window.width * window.height; i++) { + printw("%c", window.view[i]); + } + move(cursor.y, cursor.x); + + int8_t mov[2]; + int8_t del[1]; + int8_t ins[2]; + int input = getch(); + if (mode == NORMAL_MODE) { + switch (input) { + case '': + quit = 1; + break; + case 'i': + mode = INSERT_MODE; + break; + case 'h': + mov[0] = OP_MOVE1; + mov[1] = -1; + write(s, mov, 2); + cursor.move_left(); + break; + case 'l': + mov[0] = OP_MOVE1; + mov[1] = 1; + write(s, mov, 2); + cursor.move_right(); + break; + } + } else { + switch (input) { + case '': + mode = NORMAL_MODE; + break; + case KEY_BACKSPACE: + del[0] = OP_DELETE; + write(s, del, 1); + break; + default: + ins[0] = OP_INSERT; + ins[1] = input; + write(s, ins, 2); + } + } + } + + close(s); + + endwin(); + return 0; +} diff --git a/src/server/buffer.cpp b/src/server/buffer.cpp new file mode 100644 index 0000000..7b4c92d --- /dev/null +++ b/src/server/buffer.cpp @@ -0,0 +1,45 @@ +struct Buffer { + const char *name; + Page *storage; + + Buffer(const char *name) : name(name), storage(new Page()) {} + + void free_storage() { + while (storage) { + Page *iter = storage; + storage = storage->next; + delete iter; + } + } + + void read_file(const char *pathname) { + free_storage(); + int file = open(pathname, O_RDONLY); + storage = new Page(); + int bytes_read = read(file, storage->elements, PAGE_SIZE); + Page *iter = storage; + while (bytes_read > 0) { + iter->gap_start = bytes_read; + iter->element_count = bytes_read; + iter->next = new Page(); + iter->next->prev = iter; + iter = iter->next; + bytes_read = read(file, iter->elements, PAGE_SIZE); + } + if (iter->element_count == 0) { + delete iter; + } + close(file); + } + + void write_file(const char *pathname) { + int file = open(pathname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + Page *iter = storage; + while (iter) { + write(file, iter->elements, iter->gap_start); + write(file, iter->elements + iter->gap_end, PAGE_SIZE - iter->gap_end); + iter = iter->next; + } + close(file); + } +}; diff --git a/src/server/client.cpp b/src/server/client.cpp new file mode 100644 index 0000000..bed62c5 --- /dev/null +++ b/src/server/client.cpp @@ -0,0 +1,93 @@ +#define MAX_MSG_SIZE 128 +#define pos(x, y) (x) + (y) * window_w + +struct Client { + int sockfd; + Point cursor; + Point window_start; + + Client(const Buffer &b) : cursor(b), window_start(cursor) {} + + void parse_message() { + int8_t message[MAX_MSG_SIZE] = {}; + read(sockfd, message, MAX_MSG_SIZE - 1); + int8_t *iter = message; + while (*iter) { + switch (*iter) { + case OP_MOVE1: + move(iter[1]); + iter += 2; + break; + case OP_MOVE2: + move(decode2(iter, 1)); + iter += 3; + break; + case OP_MOVE4: + move(decode4(iter, 1)); + iter += 5; + break; + case OP_MOVE8: + move(decode8(iter, 1)); + iter += 9; + break; + case OP_INSERT: + push(iter[1]); + iter += 2; + break; + case OP_DELETE: + pop(); + iter += 1; + break; + case OP_SHOW: + show(decode2(iter, 1), decode2(iter, 3)); + iter += 6; + break; + } + } + } + + void show(size_t window_w, size_t window_h) { + char *view = new char[window_w * window_h]; + + Point window_end(window_start); + for (int i = 0; i < window_h; i++) { + for (int j = 0; j < window_w; j++) { + view[pos(j, i)] = window_end.element(); + if (window_end.element() == '\n') { + for (int k = j + 1; k < window_w; k++) { + view[pos(k, i)] = 0; + } + j = window_w; + } else if (window_end.element() == '\t') { + for (int k = j + 1; k < j + 8; k++) { + view[pos(k, i)] = 0; + } + j = j + 7; + } + window_end++; + } + } + + write(sockfd, view, window_w * window_h); + delete[] view; + } + + void move(int64_t target) { + while (target > 0) { + cursor++; + target--; + } + while (target < 0) { + cursor--; + target++; + } + } + + void push(int8_t input) { + cursor.push(input); + } + + void pop() { + cursor.pop(); + } +}; diff --git a/src/server/page.cpp b/src/server/page.cpp new file mode 100644 index 0000000..77cf686 --- /dev/null +++ b/src/server/page.cpp @@ -0,0 +1,87 @@ +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +struct Page { + uint8_t *elements; + Page *next; + Page *prev; + uint16_t gap_start; + uint16_t gap_end; + uint16_t element_count; + + Page() { + elements = new uint8_t[PAGE_SIZE]; + gap_start = 0; + gap_end = PAGE_SIZE; + element_count = 0; + next = nullptr; + prev = nullptr; + } + + ~Page() { + if (prev) prev->next = next; + if (next) next->prev = prev; + delete[] elements; + } + + void split() { + Page *front = new Page(); + + memcpy(front->elements, elements + PAGE_SIZE / 2, PAGE_SIZE / 2); + + front->gap_start = PAGE_SIZE / 2; + front->gap_end = PAGE_SIZE; + front->element_count = PAGE_SIZE / 2; + + gap_start = PAGE_SIZE / 2; + gap_end = PAGE_SIZE; + element_count = PAGE_SIZE / 2; + + if (next) { + next->prev = front; + } + front->next = next; + front->prev = this; + next = front; + } + + void copy_to(Page *dest) { + memcpy(dest->elements, elements, PAGE_SIZE); + dest->gap_start = gap_start; + dest->gap_end = gap_end; + dest->element_count = element_count; + } + + void move_gap_forward() { + elements[gap_start] = elements[gap_end]; + gap_start++; + gap_end++; + } + + void move_gap_backward() { + gap_end--; + gap_start--; + elements[gap_end] = elements[gap_start]; + } + + void push(uint8_t c) { + elements[gap_start] = c; + gap_start++; + element_count++; + } + + void pop() { + gap_start--; + element_count--; + } + + bool is_empty() { + return element_count == 0; + } + + bool is_full() { + return gap_start == gap_end; + } + +}; diff --git a/src/server/point.cpp b/src/server/point.cpp new file mode 100644 index 0000000..ab9f70f --- /dev/null +++ b/src/server/point.cpp @@ -0,0 +1,139 @@ +#include <stdint.h> +#include <stdbool.h> + +struct Point { + Page *page; + uint16_t index; + + uint16_t index_to_offset() { + if (index < page->gap_start) { + return index; + } else { + return index + (page->gap_end - page->gap_start); + } + } + + uint8_t prev_byte() { + if (index == 0) { + if (page->prev) { + return page->prev->elements[Point(page->prev, page->prev->element_count - 1).index_to_offset()]; + } else { + return 0; + } + } else { + return page->elements[Point(page, index - 1).index_to_offset()]; + } + } + + uint8_t next_byte() { + if (index == page->element_count) { + if (page->next) { + return page->next->elements[Point(page->next, 0).index_to_offset()]; + } else { + return 0; + } + } else { + return page->elements[index_to_offset()]; + } + } + + void move_forward() { + if (index < page->element_count) { + index++; + } else if (page->next) { + page = page->next; + index = 0; + } + } + + void move_backward() { + if (index > 0) { + index--; + } else if (page->prev) { + page = page->prev; + index = page->element_count; + } + } + + + public: + + Point() : page(0), index(0) {} + Point(Page* page, uint16_t index) : page(page), index(index) {} + Point(const Buffer &b) : page(b.storage), index(0) {} + Point(const Point &p) : page(p.page), index(p.index) {} + + bool operator==(Point p) { + return page == p.page && index == p.index; + } + + bool operator!=(Point p) { + return page != p.page || index != p.index; + } + + bool at_start() { + return index == 0 && !page->prev; + } + + bool at_end() { + return index == page->element_count && !page->next; + } + + void operator++(int) { + if (index == page->element_count) move_forward(); + move_forward(); + } + + void operator--(int) { + move_backward(); + if (index == 0) move_backward(); + } + + char element() { + return next_byte(); + } + + void align_gap() { + while (page->gap_end < index_to_offset()) { + page->move_gap_forward(); + } + while (page->gap_end > index_to_offset()) { + page->move_gap_backward(); + } + } + + void push(uint8_t c) { + if (page->is_full()) { + page->split(); + if (index >= PAGE_SIZE / 2) { + page = page->next; + index -= PAGE_SIZE / 2; + } + } + align_gap(); + page->push(c); + move_forward(); + } + + void pop() { + if (!at_start()) { + align_gap(); + page->pop(); + move_backward(); + if (index == 0) { + move_backward(); + } + } + if (page->is_empty()) { + if (page->prev) { + move_backward(); + delete page->next; + } else if (page->next) { + page->next->copy_to(page); + delete page->next; + index = 0; + } + } + } + +}; |