summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/cursor.cpp13
-rw-r--r--src/client/window.cpp16
-rw-r--r--src/common/ipc.cpp40
-rw-r--r--src/jet.cpp72
-rw-r--r--src/jetc.cpp98
-rw-r--r--src/server/buffer.cpp45
-rw-r--r--src/server/client.cpp93
-rw-r--r--src/server/page.cpp87
-rw-r--r--src/server/point.cpp139
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;
+ }
+ }
+ }
+
+};