| #include <u.h> |
| #include <libc.h> |
| #include <venti.h> |
| #include "cvt.h" |
| |
| static int |
| checksize(int n) |
| { |
| if(n < 256) { |
| werrstr("bad block size %#ux", n); |
| return -1; |
| } |
| return 0; |
| } |
| |
| // _VtEntryBig integer format is floating-point: |
| // (n>>5) << (n&31). |
| // Convert this number; must be exact or return -1. |
| int |
| vttobig(ulong n) |
| { |
| int shift; |
| ulong n0; |
| |
| n0 = n; |
| shift = 0; |
| while(n >= (1<<(16 - 5))) { |
| if(n & 1) |
| return -1; |
| shift++; |
| n >>= 1; |
| } |
| |
| n = (n<<5) | shift; |
| if(((n>>5)<<(n&31)) != n0) |
| sysfatal("vttobig %#lux => %#lux failed", n0, n); |
| return n; |
| } |
| |
| void |
| vtentrypack(VtEntry *e, uchar *p, int index) |
| { |
| ulong t32; |
| int flags; |
| uchar *op; |
| int depth; |
| int psize, dsize; |
| |
| p += index * VtEntrySize; |
| op = p; |
| |
| depth = e->type&VtTypeDepthMask; |
| flags = (e->flags&~(_VtEntryDir|_VtEntryDepthMask)); |
| flags |= depth << _VtEntryDepthShift; |
| if(e->type - depth == VtDirType) |
| flags |= _VtEntryDir; |
| U32PUT(p, e->gen); |
| p += 4; |
| psize = e->psize; |
| dsize = e->dsize; |
| if(psize >= (1<<16) || dsize >= (1<<16)) { |
| flags |= _VtEntryBig; |
| psize = vttobig(psize); |
| dsize = vttobig(dsize); |
| if(psize < 0 || dsize < 0) |
| sysfatal("invalid entry psize/dsize: %ld/%ld", e->psize, e->dsize); |
| } |
| U16PUT(p, psize); |
| p += 2; |
| U16PUT(p, dsize); |
| p += 2; |
| U8PUT(p, flags); |
| p++; |
| memset(p, 0, 5); |
| p += 5; |
| U48PUT(p, e->size, t32); |
| p += 6; |
| memmove(p, e->score, VtScoreSize); |
| p += VtScoreSize; |
| |
| assert(p-op == VtEntrySize); |
| } |
| |
| int |
| vtentryunpack(VtEntry *e, uchar *p, int index) |
| { |
| uchar *op; |
| |
| p += index * VtEntrySize; |
| op = p; |
| |
| e->gen = U32GET(p); |
| p += 4; |
| e->psize = U16GET(p); |
| p += 2; |
| e->dsize = U16GET(p); |
| p += 2; |
| e->flags = U8GET(p); |
| p++; |
| if(e->flags & _VtEntryBig) { |
| e->psize = (e->psize>>5)<<(e->psize & 31); |
| e->dsize = (e->dsize>>5)<<(e->dsize & 31); |
| } |
| e->type = (e->flags&_VtEntryDir) ? VtDirType : VtDataType; |
| e->type += (e->flags & _VtEntryDepthMask) >> _VtEntryDepthShift; |
| e->flags &= ~(_VtEntryDir|_VtEntryDepthMask|_VtEntryBig); |
| p += 5; |
| e->size = U48GET(p); |
| p += 6; |
| memmove(e->score, p, VtScoreSize); |
| p += VtScoreSize; |
| |
| assert(p-op == VtEntrySize); |
| |
| if(!(e->flags & VtEntryActive)) |
| return 0; |
| |
| /* |
| * Some old vac files use psize==0 and dsize==0 when the |
| * file itself has size 0 or is zeros. Just to make programs not |
| * have to figure out what block sizes of 0 means, rewrite them. |
| */ |
| if(e->psize == 0 && e->dsize == 0 |
| && memcmp(e->score, vtzeroscore, VtScoreSize) == 0){ |
| e->psize = 4096; |
| e->dsize = 4096; |
| } |
| if(checksize(e->psize) < 0 || checksize(e->dsize) < 0) |
| return -1; |
| |
| return 0; |
| } |
| |