summaryrefslogtreecommitdiff
path: root/memview.c
blob: 4491f055c08b9c4e9679ac3e2bd3d7463c7e341f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <SDL.h>

#include "memview.h"
#include "procfs.h"

struct viewer {
	int fd;
	SDL_Renderer *renderer;
	SDL_Texture *page_texture;
	uint8_t page_buffer[PAGE_SIZE];
};

#define BIT_OF(X, I) (((X) >> (I)) & 1ULL)

static uintptr_t zorder(int x, int y) {
	uintptr_t z = 0;
	unsigned int ux, uy;
	memcpy(&ux, &x, sizeof(unsigned int));
	memcpy(&uy, &y, sizeof(unsigned int));
	// interleaving bits results in a point on the z-order curve
	for (int i = 0; i < sizeof(x) * CHAR_BIT; i++)
		z |= (BIT_OF(ux, i) << i*2) | (BIT_OF(uy, i) << (i*2 + 1));
	return z;
}

static void unzorder(uintptr_t z, int *x, int *y) {
	unsigned int ux, uy = 0;
	for (int i = 0; i < sizeof(z) * CHAR_BIT / 2; i++) {
		ux |= BIT_OF(z, i*2) << i;
		uy |= BIT_OF(z, i*2 + 1) << i;
	}
	memcpy(x, &ux, sizeof(int));
	memcpy(y, &uy, sizeof(int));
}

uintptr_t to_addr(int x, int y) {
	uintptr_t page = zorder(x / PAGE_WIDTH, y / PAGE_HEIGHT);
	int offset = x % PAGE_WIDTH + y % PAGE_HEIGHT * PAGE_WIDTH;
	return page * PAGE_SIZE + offset;
}

void to_pos(uintptr_t addr,  int *x, int *y) {
	int px, py;
	unzorder(addr / PAGE_SIZE, &px, &py);
	int offset = addr % PAGE_SIZE;
	*x = px * PAGE_WIDTH + offset % PAGE_WIDTH;
	*y = py * PAGE_HEIGHT + offset / PAGE_WIDTH;
}

struct viewer *create_viewer(int fd, SDL_Renderer *renderer) {
	struct viewer *v = calloc(1, sizeof(struct viewer));
	if (!v) return NULL;
	v->fd = fd; v->renderer = renderer;
	v->page_texture = SDL_CreateTexture(v->renderer,
			SDL_PIXELFORMAT_RGBA32,
			SDL_TEXTUREACCESS_STREAMING,
			PAGE_WIDTH, PAGE_HEIGHT);
	if (!v->page_texture) {free(v); return NULL;}
	return v;
}

void destroy_viewer(struct viewer *v) {
	SDL_DestroyTexture(v->page_texture);
	free(v);
}

static void render_page(SDL_Texture *tex, uint8_t *data) {
	uint32_t *pixels; int pitch;
	assert(SDL_LockTexture(tex, NULL, (void **) &pixels, &pitch) != -1);
	if (data) {
		for (int i = 0; i < PAGE_SIZE; i++) {
			for (int j = 0; j < CHAR_BIT; j++) {
				uint8_t bit = (data[i] >> j) & 1;
				pixels[i * CHAR_BIT + j] = bit ? 0xFFFFFFFF : 0xFF000000;
			}
		}
	} else {
		for (int i = 0; i < PAGE_WIDTH * PAGE_HEIGHT; i++)
			pixels[i] =
				((i / 16) + i / PAGE_WIDTH / 16) % 2 ? 0xFF111111 : 0xFF333333;
	}
	SDL_UnlockTexture(tex);
}

static uint64_t fnv(uint8_t *data, size_t size) {
	uint64_t hash = 0xcbf29ce484222325;
	for (size_t i = 0; i < size; i++) {
		hash ^= data[i];
		hash *= 0x100000001b3;
	}
	return hash;
}

static uint64_t hashes[1024] = {0};

bool render_pages(struct viewer *v,
		int x, int y, int width, int height, double scale, bool refresh) {
	int page_x = x / PAGE_WIDTH;
	int page_y = y / PAGE_HEIGHT;

	int offs_x = x % PAGE_WIDTH * scale;
	int offs_y = y % PAGE_HEIGHT * scale;

	int page_width = PAGE_WIDTH * scale;
	int page_height = PAGE_HEIGHT * scale;

	int view_width = width / page_width;
	int view_height = height / page_height;

	bool present = false;
	for (int draw_y = -1; draw_y <= view_height + 1; draw_y++) {
		for (int draw_x = -1; draw_x <= view_width + 1; draw_x++) {
			SDL_Rect src = {0, 0, PAGE_WIDTH, PAGE_HEIGHT};
			SDL_Rect dest = {
				draw_x * page_width - offs_x, draw_y * page_height - offs_y,
				page_width, page_height
			};
			uintptr_t page = zorder(page_x + draw_x, page_y + draw_y);
			bool success = read_page(v->fd, page, v->page_buffer) != -1;

			uint64_t hash = fnv(v->page_buffer, success ? PAGE_SIZE : 0);
			if (refresh || hashes[page % 1024] != hash) {
				render_page(v->page_texture, success ? v->page_buffer : NULL);
				SDL_RenderCopy(v->renderer, v->page_texture, &src, &dest);
				present = true;
			}
			hashes[page % 1024] = hash;
		}
	}
	return present;
}