Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/sys/src/cmd/omero/block.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <draw.h>
#include <mouse.h>
#include <cursor.h>
#include <keyboard.h>
#include <frame.h>
#include <9p.h>
#include "gui.h"

/* CAUTION:
 * Tblock routines always measures sizes in runes.
 * Below, pos means a position measured in runes,
 * buf off means an offset measured in (UTF8) bytes.
 *
 * Iteration is usually done with blookseek and
 * routines accepting block, n, ... args. There, n
 * is the position within a Tblock node.
 * For complex navigation, packblock() ensures that
 * all the block is contiguous.
 */

enum {
	Ndump = 30,
	Nmin = 1024,
};

int blockdebug;

int
Tfmt(Fmt* f)
{
	Tblock*	b;

	b = va_arg(f->args, Tblock*);
	if (b == nil)
		return fmtprint(f, "<nil block>\n");
	else{
		return fmtprint(f, "block: %d of %d runes [%.*S%s]\n",
			b->nr, b->ar, b->nr > Ndump ? Ndump : b->nr, b->r,
			b->nr > Ndump ? "..." : "");
	}
}

void
blockdump(Tblock* b)
{
	int	i;

	for(i = 0; b; i++){
		print("%p:%d %T\n", b->block0, i, b);
		b = b->next;
	}
	print("\n");
}

	
Tblock*
newblock(Rune* r, int nr)
{
	Tblock*	b;

	b = emalloc9p(sizeof(Tblock));
	memset(b, 0, sizeof(Tblock));
	b->block0 = b;
	if (r && !nr){
		free(r);
		r = nil;
	}
	if (r){
		b->r = r;
		b->nr= b->ar = nr;
	} else {
		b->nr = 0;
		b->ar = Nblock;
		b->r = emalloc9p(Nblock*sizeof(Rune));
	}
	return b;
}

void
blockfree(Tblock* b)
{
	Tblock*	nb;

	while(b){
		nb = b;
		b = b->next;
		assert(nb->ar || nb->r == nil);
		free(nb->r);
		free(nb);
	}
}

long
blocklen(Tblock* b)
{
	long	l;

	l = 0;
	while(b){
		assert(b->nr <= b->ar);
		l += b->nr;
		b = b->next;
	}
	return l;
}

void
packblock(Tblock* b)
{
	Tblock*	nb;
	long	l;

	if (b->next == nil)
		return;

	l = blocklen(b);
	if (l >= b->ar){
		b->r = erealloc9p(b->r, (l + Nblock) * sizeof(Rune));
		b->ar= l + Nblock;
	}
	for(nb = b->next; nb != nil; nb = nb->next){
		if (nb->nr > 0){
			memmove(&b->r[b->nr], nb->r, nb->nr*sizeof(Rune));
			b->nr += nb->nr;
		}
	}
	b->r[b->nr] = 0;
	blockfree(b->next);
	b->next = nil;
}

long
blocksize(Tblock* b)
{
	long	l;

	l = 0;
	while(b){
		if (b->nr)
			l += runenlen(b->r, b->nr);
		b = b->next;
	}
	return l;
}

long
pos2off(Tblock* b, long pos)
{
	long	n, i;

	if (pos < 0)
		return pos;
	for(n = 0; pos > 0 && b != nil; b = b->next)
		for (i = 0; pos > 0 && i < b->nr; i++){
			n += runelen(b->r[i]);
			pos--;
		}
	return n;
}

long
off2pos(Tblock* b, long off)
{
	long	n, i;
	
	if (off < 0)
		return off;
	for(n = 0; off > 0 && b != nil; b = b->next)
		for (i = 0; off > 0 && i < b->nr; i++){
			off -= runelen(b->r[i]);
			n++;
		}
	return n;
}

/* It's ok to seek to blocklen(b). That places us
 * right after the last valid rune.
 * It's not ok to seek past that value.
 */
Tblock*
blockseek(Tblock* b, int *np, int off)
{
	Tblock**lb;

	assert(off >= 0);
	bdprint("blockseek %d →", off);
	lb = nil;
	do {
		if (b->nr == 0 && lb){
			*lb = b->next;
			free(b->r);
			free(b);
			b = *lb;
			continue;
		} 
		if (off <= b->nr)
			break;
		off -= b->nr;
		lb = &b->next;
		b = b->next;
	} while(b);
	*np = off;
	while(b && lb && b->nr == 0){
		*lb = b->next;
		free(b->r);
		free(b);
		b = *lb;
	}
	assert(!b || (*np >= 0 && *np <= b->nr));	// i == b->nr is ok.
	bdprint("#%d of %T\n", off, b);
	return b;
}

static void
_blockget(Tblock* b, int off, int len, Rune* rp, int del)
{
	int	edel;
	int	ndel;
	int	n;

	assert(off >= 0 && off <= b->ar);
	bdprint("_blockget %d %d from %T\n", off, len, b);
	do {
		edel = off + len;
		assert(edel > 0);
		if (edel > b->nr)
			edel = b->nr;
		n = b->nr - edel;
		ndel = edel - off;
		if (ndel > 0 && rp){
			memmove(rp, b->r + off, ndel*sizeof(Rune));
			rp += ndel;
		}
		if (del){
			if (n > 0)
				memmove(b->r + off, b->r + edel, n*sizeof(Rune));
			b->nr -= ndel;
		}
		len -= ndel;
		off = 0;
		b = b->next;
	} while(b && len > 0);
}

void
blockget(Tblock* b, int n, int len, Rune* rp)
{
	if (len)
		_blockget(b, n, len, rp, 0);
}

static Rune*
putinblock(Tblock* b, int off, Rune* r, int len)
{
	int	ntrail;

	assert(b->nr + len <= b->ar);
	ntrail = b->nr - off;
	if (ntrail > 0)
		memmove(b->r + (off + len), b->r + off, ntrail*sizeof(Rune));
	memmove(b->r + off, r, len*sizeof(Rune));
	b->nr += len;
	return r + len;
}

static void
split(Tblock* b, int off)
{
	Tblock*	t;
	Rune*	r;
	int	nr;

	assert(off >= 0 && off <= b->ar);
	if (b->nr > off){
		nr = b->nr - off;
		if (nr < Nmin)
			nr = Nmin;
		r = emalloc9p(nr * sizeof(Rune));
		memmove(r, b->r + off, (b->nr - off) * sizeof(Rune));
		t = newblock(r, b->nr - off);
		t->ar = nr;
		t->block0 = b->block0;
		b->nr = off;
		t->next = b->next;
		b->next = t;
	}
}

void
blockins(Tblock* b, int off, Rune* r, int len)
{
	int	n;
	Tblock*	t;
	Rune*	ar;
	int	an;

	assert(off >= 0 && off <= b->nr);
	bdprint("blockins %d [%.*S] into %T\n", off, b->nr, r, b);
	assert(len > 0);
	if (b->nr + len <= b->ar){
		putinblock(b, off, r, len);
		return;
	}
	split(b, off);
	do {
		n = len;
		if (b->nr == 0 && b->ar){
			if (n > b->ar)
				n = b->ar;
			r = putinblock(b, 0, r, n);
		} else {
			if (n < Nmin)
				an = Nmin;
			else
				an = n;
			ar = emalloc9p(an * sizeof(Rune));
			memmove(ar, r, n * sizeof(Rune));
			t = newblock(ar, n);
			t->ar = an;
			t->block0 = b->block0;
			t->next = b->next;
			b->next = t;
			b = t;
		}
		len -= n;
	} while(len > 0);
}

void
blockcat(Tblock* b, Tblock* nb)
{
	blockfree(b->next);
	b->next = nb;
	while(nb){
		nb->block0 = b->block0;
		nb = nb->next;
	}
}

void
blockdel(Tblock* b, int n, int len, Rune* rp)
{
	_blockget(b, n, len, rp, 1);
}

Tblock*
str2block(char* s)
{
	Rune*	r;
	int	nr;

	r = utf2runes(s, nil, nil);
	nr= runeslen(r);
	return newblock(r, nr);
}

char*
block2str(Tblock* bl, long* l)
{
	char*	s;
	char*	bs;
	int	n;
	Tblock*	b;
	int	i;
	long	nc, nr;

	n = blocksize(bl);
	s = malloc(n+1);
	s[n] = 0;
	bs = s;
	nc = 0;
	for(b = bl; b != nil; b = b->next){
		for (i = 0; i < b->nr; i++){
			nr = runetochar(bs, b->r+i);
			bs += nr;
			nc += nr;
		}
	}
	*bs = 0;
	if (l)
		*l = nc;
	return s;
}

Rune*
utf2runes(char* s, int* lp, int* nlines)
{
	Rune*	r;
	int	n;
	int	l;
	int	wc;
	char*	se;
	int	lc;

	if (!lp)
		l = utflen(s);
	else {
		if (*lp)
			l = utfnlen(s, *lp);
		else
			l = utflen(s);
		*lp = 0;
	}
	if (nlines)
		*nlines = 0;
	r = emalloc9p(sizeof(Rune)*(l+1));
	setmalloctag(r, getcallerpc(&s));
	se = s;
	lc = 0;
	for(n = 0; n < l; n++){
		wc = chartorune(r+n, se);
		se += wc;
		if ((r[n] == '\n' || ++lc > 80) && nlines){
			lc = 0;
			(*nlines)++;
		}
	}
	r[l] = 0;
	if (lp)
		*lp = se - s;
	return r;
}

int
runeslen(Rune* r)
{
	int	n;

	for (n = 0; *r; r++, n++)
		;
	return n;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.