<|start_filename|>parser/tiff/parser.go<|end_filename|>
package tiff

import (
	"fmt"
	"github.com/emilyselwood/tiffhax/parser"
	"github.com/emilyselwood/tiffhax/payload"
	"io"
)

func ParseFile(in io.ReadSeeker) ([]payload.Section, error) {

	start, end, err := findExtents(in)
	if err != nil {
		return nil, fmt.Errorf("could not find reader extents: %v", err)
	}

	startRegion := parser.Unknown{
		Start:    start,
		End:      end,
		Children: []parser.Region{},
	}

	// start by parsing the header
	header, l, err := ParseHeader(in)
	if err != nil {
		return returnError(&startRegion, fmt.Errorf("could not parse header, %v", err))
	}
	if err := insert(&startRegion, header, 0, l); err != nil {
		return returnError(&startRegion, fmt.Errorf("could not insert header %v", err))
	}

	var offsets []*Offset
	var data []*Data
	// start with the first IFD (there must be at least one)
	ifd, offset, d, err := readIFD(in, header, header.FirstIFDOffset)
	if err != nil {
		return returnError(&startRegion, fmt.Errorf("could not read first ifd, %v", err))
	}

	if err := insert(&startRegion, ifd, ifd.Start, ifd.End); err != nil {
		return returnError(&startRegion, fmt.Errorf("could not insert ifd, %v", err))
	}
	offsets = append(offsets, offset...)
	data = append(data, d...)

	for ifd.Next != 0 {
		ifd, offset, d, err = readIFD(in, header, int64(ifd.Next))
		if err != nil {
			return returnError(&startRegion, fmt.Errorf("could not read ifd, %v", err))
		}

		if err := insert(&startRegion, ifd, ifd.Start, ifd.End); err != nil {
			return returnError(&startRegion, fmt.Errorf("could not insert ifd, %v", err))
		}
		offsets = append(offsets, offset...)
		data = append(data, d...)
	}

	// now handle the offsets
	// because an offset can point to a list of offsets we need to keep handling them till we are done.
	for _, o := range offsets {
		d, err := o.Parse(in, header.Endian)
		if err != nil {
			return returnError(&startRegion, fmt.Errorf("could not parse offset, %v", err))
		}
		if err := insert(&startRegion, o, o.Start, o.End); err != nil {
			return returnError(&startRegion, fmt.Errorf("could not insert offset result, %v", err))
		}
		data = append(data, d...)
	}

	// Finally we need to handle the data sections.
	// Going to need to be able to work out:
	//  a: where the strips start
	//  b: how big each strip is.

	for _, d := range data {
		err := d.Parse(in, header.Endian)
		if err != nil {
			return returnError(&startRegion, fmt.Errorf("could not parse data information, %v", err))
		}
		if err := insert(&startRegion, d, d.Start, d.End); err != nil {
			return returnError(&startRegion, fmt.Errorf("could not insert data result, %v", err))
		}
	}


	return startRegion.Render()
}

func returnError(startRegion parser.Region, inErr error) ([]payload.Section, error) {
	res, err := startRegion.Render();
	if err != nil {
		return res, fmt.Errorf("%v, additionaly while rendering the output the following happend: %v", inErr, err)
	}
	return res, inErr
}

func readIFD(in io.ReadSeeker, header *Header, offset int64) (*IFD, []*Offset, []*Data, error) {
	_, err := in.Seek(offset, io.SeekStart)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("could not seek to IFD, %v", err)
	}

	ifd, _, offsets, d, err := ParseIFD(in, offset, header.Endian)
	if err != nil {
		return  nil, nil, nil, fmt.Errorf("could not parse IFD, %v", err)
	}

	return ifd, offsets, d, nil
}

func insert(top parser.Region, newBit parser.Region, start int64, end int64) error {
	target, err := top.Find(start)
	if err != nil {
		return fmt.Errorf("could not insert new region, %v", err)
	}
	if err := target.Split(start, end, newBit); err != nil {
		return fmt.Errorf("could not split, %v", err)
	}

	return nil
}

func findExtents(in io.ReadSeeker) (int64, int64, error) {
	end, err := in.Seek(0, io.SeekEnd)
	if err != nil {
		return 0, 0, fmt.Errorf("could not seek to end of file %v", err)
	}
	start, err := in.Seek(0, io.SeekStart)
	if err != nil {
		return 0, 0, fmt.Errorf("could not seek to start of file %v", err)
	}

	return start, end, nil
}



<|start_filename|>payload/render.go<|end_filename|>
package payload

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"strconv"
	"strings"
)

func RenderTemplate(t string, data interface{}, funcMap template.FuncMap) (string, error) {
	tmpl, err := template.New("template").Funcs(funcMap).Parse(t)
	if err != nil {
		return "", fmt.Errorf("could not parse template, %v", err)
	}

	var descBuffer bytes.Buffer
	if err := tmpl.Execute(&descBuffer, data); err != nil {
		return "", fmt.Errorf("could not render template, %v", err)
	}

	return descBuffer.String(), nil
}

func RenderByteBlocks(target io.StringWriter, in []byte, length int, classes []string) {
	done := false
	start := 0
	end := length
	i := 0
	for !done {
		if i > 0 {
			_, _ = target.WriteString(" ")
		}
		_, _ = target.WriteString("<span class=\"")
		_, _ = target.WriteString(classes[i % len(classes)])
		_, _ = target.WriteString("\">")
		for j, b := range in[start:end] {
			if j > 0 {
				_, _ = target.WriteString(" ")
			}
			_, _ = target.WriteString(RenderByte(b))
			i++
			if i == 16 {
				_, _ = target.WriteString("<br />")
				i = 0
			}
		}
		_, _ = target.WriteString("</span>")

		start = end
		end = end + length
		if end > len(in) {
			end = len(in)
		}
		if start >= len(in) {
			done = true
		}
	}
}

/*
RenderBytesSpan writes a byte array formatted as hex with a surrounding span.
This is useful for highlighting sections of the data.
 */
func RenderBytesSpan(target io.StringWriter, in []byte, class string) io.StringWriter {
	_, _ = target.WriteString("<span class=\"")
	_, _ = target.WriteString(class)
	_, _ = target.WriteString("\">")
	_, _ = target.WriteString(RenderBytes(in))
	_, _ = target.WriteString("</span>")

	return target
}

/*
RenderBytes displays an array of bytes in hex with spaces between each byte
 */
func RenderBytes(in []byte) string {
	var buffer bytes.Buffer
	if len(in) > 0 {
		buffer.WriteString(RenderByte(in[0]))
		if len(in) > 1 {
			i := 1
			for _, b := range in[1:] {
				buffer.WriteString(" ")

				buffer.WriteString(RenderByte(b))
				i++
				if i == 16 {
					buffer.WriteString("<br />")
					i = 0
				}
			}
		}
	}

	return strings.ToUpper(buffer.String())
}

func RenderByte(in byte) string {
	result := strconv.FormatInt(int64(in), 16)
	if len(result) < 2 {
		result = "0" + result
	}
	return result
}

<|start_filename|>parser/tiff/data.go<|end_filename|>
package tiff

import (
	"encoding/binary"
	"fmt"
	"github.com/emilyselwood/tiffhax/parser"
	"github.com/emilyselwood/tiffhax/parser/tiff/constants"
	"github.com/emilyselwood/tiffhax/payload"
	"html/template"
	"io"
)

type Data struct {
	IFD   *IFD
	Start int64
	End   int64
	DType uint16
	Count int64
	I     int
}

func (d *Data) Parse(in io.ReadSeeker, order binary.ByteOrder) error {
	byteCounts, err := d.fetchFieldValue(279, in, order) // strip byte counts
	if err != nil {
		// try for the other field
		byteCounts, err = d.fetchFieldValue(325, in, order)
		if err != nil {
			return fmt.Errorf("could not find rows per strip field, %v", err)
		}
	}

	d.End = d.Start + byteCounts

	return nil
}

func (d *Data) Contains(offset int64) bool {
	return d.Start <= offset && offset < d.End
}

func (d *Data) ContainsRegion(start int64, end int64) bool {
	return d.Start <= start && start < d.End && d.Start < end && end < d.End
}

func (d *Data) Find(offset int64) (parser.Region, error) {
	if offset < d.Start || offset >= d.End {
		return nil, fmt.Errorf("find offset %v outside of offset region %v to %v", offset, d.Start, d.End)
	}
	return d, nil
}

func (d *Data) Split(start int64, end int64, newBit parser.Region) error {
	return fmt.Errorf("data can not be split")
}

func (d *Data) Render() ([]payload.Section, error) {
	return []payload.Section{
		&payload.General{
			Start:   d.Start,
			End:     d.End - 1,
			Id:      "data",
			TheData: template.HTML("data hidden for size"),
			Text:    template.HTML("A block of image data"),
		},
	}, nil
}

func (d *Data) fetchFieldValue(id uint16, in io.ReadSeeker, order binary.ByteOrder) (int64, error) {
	field, err := d.IFD.FindField(id)
	if err != nil {
		return 0, fmt.Errorf("could not find field %v, %v", id, err)
	}

	if field.IsOffset {
		// now we need to go and find the lookup value for d.I
		// work out where we need to jump to

		pos := int64(field.Value) + int64(constants.DataTypeSize[field.DType] * uint32(d.I))

		_, err := in.Seek(pos, io.SeekStart)
		if err != nil {
			return 0, fmt.Errorf("could not seek to find offset value, %v", err)
		}

		buf := make([]byte, constants.DataTypeSize[field.DType])
		n, err := in.Read(buf)
		if err != nil {
			return 0, fmt.Errorf("could not read offset value, %v", err)
		}
		if n != int(constants.DataTypeSize[field.DType]) {
			return 0, fmt.Errorf("reading offset got the wrong number of bytes, expected %v got %v",constants.DataTypeSize[field.DType], n)
		}
		return int64(ReadBuffer(buf, order)), nil


	}

	return int64(field.Value), nil
}


func ReadBuffer(buf []byte, order binary.ByteOrder) uint32 {
	len := len(buf)

	if len == 2 {
		return uint32(order.Uint16(buf))
	}
	if len == 4 {
		return order.Uint32(buf)
	}
	return 0
}

<|start_filename|>parser/tiff/header.go<|end_filename|>
package tiff

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/emilyselwood/tiffhax/parser"
	"github.com/emilyselwood/tiffhax/payload"
	"html/template"
	"io"
)


type Header struct {
	Start          int64
	End            int64
	Data           []byte
	Endian         binary.ByteOrder
	BigTiff        bool
	FirstIFDOffset int64
}

func ParseHeader(in io.Reader) (*Header, int64, error) {
	data := make([]byte, 8)

	n, err := in.Read(data)
	if err != nil {
		return nil, int64(n), fmt.Errorf("could not read header, %v", err)
	}
	if n != 8 {
		return nil, int64(n), fmt.Errorf("not enough data for header")
	}

	var result Header

	result.Data = append(result.Data, data...)

	if data[0] == 'M' && data[1] == 'M' {
		result.Endian = binary.BigEndian
	} else if data[0] == 'I' && data[1] == 'I' {
		result.Endian = binary.LittleEndian
	} else {
		result.Endian = nil
	}

	magic := result.Endian.Uint16(data[2:4])
	if magic == 43{
		return &result, 8, fmt.Errorf("parsing bigtiff file not yet implemented")
	}	
	if magic != 42 {
		return &result, 8, fmt.Errorf("not a tiff file, magic number was %v expected 42", magic)
	}
	result.FirstIFDOffset = int64(result.Endian.Uint32(data[4:8]))

	result.Start = 0
	result.End = 8

	return &result, int64(len(result.Data)), nil

}

func (h *Header) Contains(offset int64) bool {
	return h.Start <= offset && offset < h.End
}

func (h *Header) ContainsRegion(start int64, end int64) bool {
	return h.Start <= start && start < h.End && h.Start < end && end < h.End
}

func (h *Header) Find(offset int64) (parser.Region, error) {
	if offset < h.Start || offset >= h.End {
		return nil, fmt.Errorf("find offset %v outside of region %v to %v", offset, h.Start, h.End)
	}
	return h, nil
}

func (h *Header) Split(start int64, end int64, newBit parser.Region) error {
	return fmt.Errorf("header can not be split")
}

func (h *Header) Render() ([]payload.Section, error) {
	desc, err := payload.RenderTemplate(headerTemplate, h, template.FuncMap{})
	if err != nil {
		return nil, fmt.Errorf("could not render header description, %v", err)
	}

	var data bytes.Buffer

	payload.RenderBytesSpan(&data, h.Data[0:2], "header_endian")
	data.WriteRune(' ')
	payload.RenderBytesSpan(&data, h.Data[2:4], "header_magic")
	data.WriteRune(' ')
	payload.RenderBytesSpan(&data, h.Data[4:8], "header_offset")

	return []payload.Section{
		&payload.General{
			Start:   h.Start,
			End:     h.End - 1,
			Id:      "header",
			TheData: template.HTML(data.String()),
			Text:    template.HTML(desc),
		},
	}, nil
}

const headerTemplate = `The header shows this is a <span class="header_magic">tiff file</span> is in <span class="header_endian">{{.Endian}}</span> format {{if .BigTiff}} and is a big tiff {{end}} The first IDF starts at byte <span class="header_offset"><a href="#{{.FirstIFDOffset}}">{{.FirstIFDOffset}}</a></span>`


<|start_filename|>parser/region.go<|end_filename|>
package parser

import (
	"fmt"
	"github.com/emilyselwood/tiffhax/payload"
	"strconv"
)

type Region interface {
	Split(start int64, end int64, newBit Region) error
	Render() ([]payload.Section, error)
	Find(offset int64) (Region, error)
	Contains(offset int64) bool
	ContainsRegion(start int64, end int64) bool
}

type Unknown struct {
	Start int64
	End   int64

	Children []Region
}

func (u *Unknown) Contains(offset int64) bool {
	return u.Start <= offset && offset < u.End
}

func (u *Unknown) ContainsRegion(start int64, end int64) bool {
	return u.Start <= start && start < u.End && u.Start < end && end <= u.End
}

func (u *Unknown) Find(offset int64) (Region, error) {
	if offset < u.Start || offset >= u.End {
		return nil, fmt.Errorf("find offset %v outside of region %v to %v", offset, u.Start, u.End)
	}

	if len(u.Children) > 0 {
		for _, c := range u.Children {
			if c.Contains(offset) {
				r, err := c.Find(offset)
				return r, err
			}
		}
		return nil, fmt.Errorf("find offset %v inside region %v to %v but not contained by children ... bork bork bork", offset, u.Start, u.End)
	} else {
		return u, nil
	}
}

func (u *Unknown) Split(start int64, end int64, newBit Region) error {
	if !u.ContainsRegion(start, end) {
		return fmt.Errorf("split region %v to %v outside of region %v to %v", start, end, u.Start, u.End)
	}
	if len(u.Children) != 0 {
		return fmt.Errorf("split region %v to %v is in %v to %v but it has children already", start, end, u.Start, u.End)
	}

	if start == u.Start && end == u.End {
		// Really we should replace this Unknown in its parent but this works for now.
		u.Children = []Region{
			newBit,
		}
	} else if start == u.Start {
		u.Children = []Region{
			newBit,
			&Unknown{
				Start: end,
				End:   u.End,
			},
		}
	} else if end == u.End {
		u.Children = []Region{
			&Unknown{
				Start: u.Start,
				End:   start,
			},
			newBit,
		}
	} else {
		u.Children = []Region{
			&Unknown{
				Start: u.Start,
				End:   start,
			},
			newBit,
			&Unknown{
				Start: end,
				End:   u.End,
			},
		}
	}

	return nil
}

func (u *Unknown) Render() ([]payload.Section, error) {
	if len(u.Children) > 0 {
		var result []payload.Section
		for _, c := range u.Children {
			part, err := c.Render()
			if err != nil {
				return nil, err
			}
			result = append(result, part...)
		}
		return result, nil
	} else {
		// TODO better rendering.
		return []payload.Section{&payload.General{Start: u.Start, End: u.End, Id: strconv.FormatInt(u.Start, 10), Text: "Un-parsed section"}}, nil
	}
}


<|start_filename|>parser/tiff/ifd.go<|end_filename|>
package tiff

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/emilyselwood/tiffhax/parser"
	"github.com/emilyselwood/tiffhax/payload"
	"html/template"
	"io"
	"reflect"
)

type IFD struct {
	Start int64
	End   int64
	HeaderData  []byte
	FooterData  []byte
	Count uint16
	Children []*Field
	Next uint32
}

func ParseIFD(in io.Reader, start int64, order binary.ByteOrder) (*IFD, int64, []*Offset, []*Data, error) {
	ifdHeader := make([]byte, 2)

	n, err := in.Read(ifdHeader)
	if err != nil {
		return nil, int64(n), nil, nil, fmt.Errorf("could not read ifd header, %v", err)
	}

	var result IFD

	result.Start = start
	result.Count = order.Uint16(ifdHeader)
	result.End = start + 6 + (int64(result.Count) * 12)
	result.HeaderData = ifdHeader

	// Now read the fields for the IFD
	var offsets []*Offset
	var data []*Data
	for i := 0; i < int(result.Count); i++ {
		fieldStart := start + 2 + (int64(i) * 12)
		field, offset, d, err := ParseField(in, fieldStart, order)
		if err != nil {
			return nil, 0, nil, nil, fmt.Errorf("could not parse field %v of ifd, %v", i, err)
		}

		result.Children = append(result.Children, field)
		if offset != nil {
			offsets = append(offsets, offset)
		}
		if d != nil {
			data = append(data, d)
		}
	}

	for _, o := range offsets {
		o.IFD = &result
	}

	for _, d := range data {
		d.IFD = &result
	}

	nextIFD := make([]byte, 4)
	n, err = in.Read(nextIFD)
	if err != nil {
		return nil, int64(n), nil, nil, fmt.Errorf("could not read ifd footer, %v", err)
	}

	result.Next = order.Uint32(nextIFD)
	result.FooterData = nextIFD
	return &result, result.End, offsets, data, nil
}

func (i *IFD) FindField(id uint16) (*Field, error) {
	for _, c := range i.Children {
		if c.ID == id {
			return c, nil
		}
	}
	return nil, fmt.Errorf("could not find field %v in ifd starting at %v", id, i.Start)
}


func (i *IFD) Contains(offset int64) bool {
	return i.Start <= offset && offset < i.End
}

func (i *IFD) ContainsRegion(start int64, end int64) bool {
	return i.Start <= start && start < i.End && i.Start < end && end < i.End
}

func (i *IFD) Find(offset int64) (parser.Region, error) {
	if offset < i.Start || offset >= i.End {
		return nil, fmt.Errorf("find offset %v outside of ifd region %v to %v", offset, i.Start, i.End)
	}
	if offset >= i.Start + 2 {
		if len(i.Children) > 0 {
			for _, c := range i.Children {
				if c.Contains(offset) {
					r, err := c.Find(offset)
					return r, err
				}
			}
			return nil, fmt.Errorf("find offset %v inside region %v to %v but not contained by children ... bork bork bork", offset, i.Start, i.End)
		} else {
			return nil, fmt.Errorf("find offset past ifd header but ifd does not have any children ... bork bork bork")
		}
	}

	return i, nil
}

func (i *IFD) Split(start int64, end int64, newBit parser.Region) error {
	return fmt.Errorf("IFD between %v and %v can not be split between %v and %v to insert a %v", i.Start, i.End, start, end, reflect.TypeOf(newBit))
}

func (i *IFD) Render() ([]payload.Section, error) {
	var result []payload.Section

	// ifd header
	header, err := i.renderHeader()
	if err != nil {
		return result, fmt.Errorf("could not render ifd header, %v", err)
	}
	result = append(result, header)

	// each field
	for _, f := range i.Children {
		childSections, err := f.Render()
		if err != nil {
			return result, fmt.Errorf("could not render ifd field, %v", err)
		}
		result = append(result, childSections...)
	}

	// ifd footer
	footer, err := i.renderFooter()
	if err != nil {
		return result, fmt.Errorf("could not render ifd footer, %v", err)
	}
	result = append(result, footer)

	return result, nil
}

func (i *IFD) renderHeader() (payload.Section, error) {

	desc, err := payload.RenderTemplate(ifdHeaderTemplate, i, template.FuncMap{})
	if err != nil {
		return nil, fmt.Errorf("could not render ifd description, %v", err)
	}

	var data bytes.Buffer
	payload.RenderBytesSpan(&data, i.HeaderData, "ifd_header")

	return &payload.General{
		Start:   i.Start,
		End:     i.Start + 1,
		Id:      "ifd",
		TheData: template.HTML(data.String()),
		Text:    template.HTML(desc),
	}, nil
}
const ifdHeaderTemplate = `The start of an IFD (Image File Directory) that contains <span class="ifd_header">{{.Count}}</span> fields`


func (i *IFD) renderFooter() (payload.Section, error) {
	var desc string
	var data bytes.Buffer

	if i.Next == 0 {
		desc = "This is the last IFD in the chain. If this <span class=\"ifd_footer\">0</span> was a number it would point to the next ifd"
	} else {
		desc = fmt.Sprintf("The next ifd can be found at offset <a href=\"#%v\"><span class=\"ifd_footer\">%v</span></a>", i.Next, i.Next)
	}

	payload.RenderBytesSpan(&data, i.FooterData, "ifd_footer")

	return &payload.General{
		Start:   i.End - 5,
		End:     i.End - 1,
		Id:      "ifd",
		TheData: template.HTML(data.String()),
		Text:    template.HTML(desc),
	}, nil
}

<|start_filename|>parser/tiff/constants/datatypes.go<|end_filename|>
package constants


var DataTypeNames = map[uint16]string {
	1: "byte",
	2: "ascii",
	3: "short",
	4: "long",
	5: "rational",
	6: "sbyte",
	7: "undefine",
	8: "sshort",
	9: "slong",
	10: "srational",
	11: "float",
	12: "double",
}

var DataTypeSize = map[uint16]uint32 {
	1: 1,
	2: 1,
	3: 2,
	4: 4,
	5: 8,
	6: 1,
	7: 1,
	8: 2,
	9: 4,
	10: 8,
	11: 4,
	12: 8,
}


<|start_filename|>parser/tiff/offset.go<|end_filename|>
package tiff

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/emilyselwood/tiffhax/parser"
	"github.com/emilyselwood/tiffhax/parser/tiff/constants"
	"github.com/emilyselwood/tiffhax/payload"
	"html/template"
	"io"
	"reflect"
)

/*
Offset holds a link from somewhere in a file to somewhere else.
*/
type Offset struct {
	From    int64
	To      int64
	Start   int64
	End     int64
	DType   uint16
	Count   uint32
	FieldId uint16
	IsData  bool
	Data    []byte
	IFD     *IFD
}


func (o *Offset) Parse(in io.ReadSeeker, order binary.ByteOrder) ([]*Data, error) {
	_, err := in.Seek(o.To, io.SeekStart)
	if err != nil {
		return nil, fmt.Errorf("could not seek to offset start %v, %v", o.To, err)
	}

	o.Start = o.To
	o.End = o.Start + (int64(o.Count) * int64(constants.DataTypeSize[o.DType]))
	if !o.IsData {
		o.Data = make([]byte, o.End - o.Start)
		n, err := in.Read(o.Data)
		if err != nil {
			return nil, fmt.Errorf("could not read data at offset %v, %v", o.To, err)
		}
		if int64(n) != o.End - o.Start {
			return nil, fmt.Errorf("did not get enough data when reading at offset %v", o.To)
		}
		return nil, nil
	}
	chunk := make([]byte, constants.DataTypeSize[o.DType])
	var data []*Data
	for i := 0; uint32(i) < o.Count; i++ {
		var d Data
		n, err := in.Read(chunk)
		if err != nil {
			return nil, fmt.Errorf("could not read offset entry, %v", err)
		}
		if uint32(n) != constants.DataTypeSize[o.DType] {
			return nil, fmt.Errorf("got wrong number of bytes from read, expected %v got %v", constants.DataTypeSize[o.DType], n)
		}

		o.Data = append(o.Data, chunk...)

		d.Start = int64(order.Uint32(chunk))
		d.IFD = o.IFD
		d.I = i
		data = append(data, &d)
	}

	return data, nil
}


func (o *Offset) Contains(offset int64) bool {
	return o.Start <= offset && offset < o.End
}

func (o *Offset) ContainsRegion(start int64, end int64) bool {
	return o.Start <= start && start < o.End && o.Start < end && end < o.End
}

func (o *Offset) Find(offset int64) (parser.Region, error) {
	if offset < o.Start || offset >= o.End {
		return nil, fmt.Errorf("find offset %v outside of offset region %v to %v", offset, o.Start, o.End)
	}
	return o, nil
}

func (o *Offset) Split(start int64, end int64, newBit parser.Region) error {
	return fmt.Errorf("Offset between %v and %v can not be split between %v and %v to insert a %v", o.Start, o.End, start, end, reflect.TypeOf(newBit))
}

func (o *Offset) Render() ([]payload.Section, error) {

	desc, err := payload.RenderTemplate(offsetTemplate, o, template.FuncMap{
		"FieldNames": func(fieldId uint16) string {
			return constants.FieldNames[fieldId]
		},
		"DataTypeNames": func(typeId uint16) string {
			return constants.DataTypeNames[typeId]
		},
		"FieldValueLookUp" : func() string {
			if o.DType == 2 {
				value := string(o.Data)
				return " which decodes to \"" + value + "\""
			}
			return ""
		},
	})
	if err != nil {
		return nil, fmt.Errorf("couldn't render offset description, %v", err)
	}

	var dataBuffer bytes.Buffer
	if len(o.Data) > 0 {
		payload.RenderByteBlocks(&dataBuffer, o.Data, int(constants.DataTypeSize[o.DType]), []string {"offset_a", "offset_b", "offset_c"})
	}

	return []payload.Section{
		&payload.General{
			Start:   o.Start,
			End:     o.End - 1,
			Id:      "offset",
			TheData: template.HTML(dataBuffer.String()),
			Text:    template.HTML(desc),
		},
	}, nil
}

const offsetTemplate = `{{.Count}} {{DataTypeNames .DType}} values for <a href="#{{.From}}">{{FieldNames .FieldId}}</a> {{ FieldValueLookUp }}`

<|start_filename|>parser/tiff/field.go<|end_filename|>
package tiff

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/emilyselwood/tiffhax/parser"
	"github.com/emilyselwood/tiffhax/parser/tiff/constants"
	"github.com/emilyselwood/tiffhax/payload"
	"html/template"
	"io"
)

type Field struct {
	Start    int64
	End      int64
	Data     []byte
	ID       uint16
	DType    uint16
	Count    uint32
	Value    uint32
	IsOffset bool
}

func ParseField(in io.Reader, start int64, order binary.ByteOrder) (*Field, *Offset, *Data, error) {
	data := make([]byte, 12)
	n, err := in.Read(data)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("could not read ifd field, %v", err)
	}
	if n != 12 {
		return nil, nil, nil, fmt.Errorf("strange size read from ifd field got %v expected 12", n)
	}

	var result Field
	result.Start = start
	result.End = start + 12
	result.Data = data

	// parse the actual data.
	result.ID = order.Uint16(data[0:2])
	result.DType = order.Uint16(data[2:4])
	result.Count = order.Uint32(data[4:8])
	result.Value = order.Uint32(data[8:12])
	// TODO: parse ascii better if its not an offset.

	// do we have an offset or a value
	if result.Count*constants.DataTypeSize[result.DType] > 4 {
		result.IsOffset = true

		var offset Offset
		offset.DType = result.DType
		offset.From = start
		offset.To = int64(result.Value)
		offset.Count = result.Count
		offset.FieldId = result.ID

		if result.ID == 273 || result.ID == 324 {
			offset.IsData = true
		}

		return &result, &offset, nil, nil
	}

	if result.ID == 273 || result.ID == 324 { // stripOffset field wasn't an offset so it must be a single pointer.
		var d Data
		d.Start = int64(result.Value)

		return &result, nil, &d, nil
	}

	return &result, nil, nil, nil
}

func (f *Field) Contains(offset int64) bool {
	return f.Start <= offset && offset < f.End
}

func (f *Field) ContainsRegion(start int64, end int64) bool {
	return f.Start <= start && start < f.End && f.Start < end && end < f.End
}

func (f *Field) Find(offset int64) (parser.Region, error) {
	if offset < f.Start || offset >= f.End {
		return nil, fmt.Errorf("find offset %v outside of field region %v to %v", offset, f.Start, f.End)
	}
	return f, nil
}

func (f *Field) Split(start int64, end int64, newBit parser.Region) error {
	return fmt.Errorf("field can not be split")
}

func (f *Field) Render() ([]payload.Section, error) {

	desc, err := payload.RenderTemplate(fieldTemplate, f, template.FuncMap{
		"FieldNames": func(fieldId uint16) string {
			return constants.FieldNames[fieldId]
		},
		"DataTypeNames": func(typeId uint16) string {
			return constants.DataTypeNames[typeId]
		},
		"FieldValueLookUp" : func() string {
			if f.IsOffset {
				return " which is an offset"
			}

			fieldValues, ok := constants.FieldValueLookup[f.ID]
			if ok {
				value, ok := fieldValues[f.Value]
				if ok {
					return " which means " + value
				}
			}
			if f.DType == 2 {
				value := string(f.Data)
				return " which decodes to " + value
			}
			return ""
		},
		// TODO: better descriptions
	})
	if err != nil {
		return nil, fmt.Errorf("could not format template for field, %v", err)
	}

	var data bytes.Buffer

	payload.RenderBytesSpan(&data, f.Data[0:2], "field_id")
	data.WriteRune(' ')
	payload.RenderBytesSpan(&data, f.Data[2:4], "field_type")
	data.WriteRune(' ')
	payload.RenderBytesSpan(&data, f.Data[4:8], "field_count")
	data.WriteRune(' ')
	payload.RenderBytesSpan(&data, f.Data[8:12], "field_value")

	return []payload.Section{
		&payload.General{
			Start:   f.Start,
			End:     f.End - 1,
			Id:      "ifd_field",
			TheData: template.HTML(data.String()),
			Text:    template.HTML(desc),
		},
	}, nil

}

const fieldTemplate = `A field called <span class="field_id">{{ FieldNames .ID}}</span> 
is <span class="field_count">{{ .Count }}</span> <span class="field_type">{{ DataTypeNames .DType }}</span> values. 
The value shows {{ if .IsOffset }}<a href="#{{ .Value }}">{{end}}<span class="field_value">{{ .Value }}</span>{{ if .IsOffset }}<a/>{{end}}
{{ FieldValueLookUp }}`


<|start_filename|>parser/tiff/constants/fieldlookup.go<|end_filename|>
package constants

var FieldValueLookup = map[uint16]map[uint32]string{
	259: { // Compression
		1:     "Uncompressed",
		2:     "CCITT 1D",
		3:     "CCITT Group 3",
		4:     "CCITT Group 4",
		5:     "LZW",
		6:     "JPEG",
		32771: "Uncompressed (deprecated)",
		32773: "PackBits",
	},
	262: { // Photometric interpretation.
		0: "WhiteIsZero",
		1: "BlackIsZero",
		2: "RGB",
		3: "RGB Palette",
		4: "Transparency Mask",
		5: "CMYK",
		6: "YCbCr", // yes there is a gap, no I don't know why
		8: "CIELab",
	},
	284: { // PlanarConfiguration
		0: "Unknown",
		1: "Chunky",
		2: "Planar",
	},
	339 : { // SampleFormat
		0: "Unknown",
		1: "Unsigned Int",
		2: "Signed Int",
		3: "Float",
		4: "Undefined",
		5: "Complex Int",
		6: "Complex Float",
	},
	// TODO: more of these
}


<|start_filename|>payload/payload.go<|end_filename|>
package payload

import (
	"fmt"
	"html/template"
	"strconv"
)

type Payload struct {
	Title    string
	FileName string
	Sections []Section
}

type Section interface {
	ID() string
	Class() string
	Offset() string
	Data() template.HTML
	Description() template.HTML
}

/*
General represents a normal section of the file
*/
type General struct {
	Start int64
	End int64
	Id string
	TheData template.HTML
	Text template.HTML
}

func (g *General) ID() string {
	return strconv.FormatInt(g.Start, 10)
}

func (g *General) Class() string {
	return g.Id
}

func (g *General) Offset() string {
	return fmt.Sprintf("%v .. %v", g.Start, g.End)
}

func (g *General) Data() template.HTML {
	return g.TheData
}

func (g *General) Description() template.HTML {
	return g.Text
}


<|start_filename|>parser/tiff/constants/fieldnames.go<|end_filename|>
package constants

// tiff ifd field names cribbed from the Library of Congress
// http://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml

var FieldNames = map[uint16]string {
	254: "NewSubfileType",
	255: "SubfileType",
	256: "ImageWidth",
	257: "ImageLength",
	258: "BitsPerSample",
	259: "Compression",
	262: "PhotometricInterpretation",
	263: "Threshholding",
	264: "CellWidth",
	265: "CellLength",
	266: "FillOrder",
	269: "DocumentName",
	270: "ImageDescription",
	271: "Make",
	272: "Model",
	273: "StripOffsets",
	274: "Orientation",
	277: "SamplesPerPixel",
	278: "RowsPerStrip",
	279: "StripByteCounts",
	280: "MinSampleValue",
	281: "MaxSampleValue",
	282: "XResolution",
	283: "YResolution",
	284: "PlanarConfiguration",
	285: "PageName",
	286: "XPosition",
	287: "YPosition",
	288: "FreeOffsets",
	289: "FreeByteCounts",
	290: "GrayResponseUnit",
	291: "GrayResponseCurve",
	292: "T4Options",
	293: "T6Options",
	296: "ResolutionUnit",
	297: "PageNumber",
	301: "TransferFunction",
	305: "Software",
	306: "DateTime",
	315: "Artist",
	316: "HostComputer",
	317: "Predictor",
	318: "WhitePoint",
	319: "PrimaryChromaticities",
	320: "ColorMap",
	321: "HalftoneHints",
	322: "TileWidth",
	323: "TileLength",
	324: "TileOffsets",
	325: "TileByteCounts",
	326: "BadFaxLines",
	327: "CleanFaxData",
	328: "ConsecutiveBadFaxLines",
	330: "SubIFDs",
	332: "InkSet",
	333: "InkNames",
	334: "NumberOfInks",
	336: "DotRange",
	337: "TargetPrinter",
	338: "ExtraSamples",
	339: "SampleFormat",
	340: "SMinSampleValue",
	341: "SMaxSampleValue",
	342: "TransferRange",
	343: "ClipPath",
	344: "XClipPathUnits",
	345: "YClipPathUnits",
	346: "Indexed",
	347: "JPEGTables",
	351: "OPIProxy",
	400: "GlobalParametersIFD",
	401: "ProfileType",
	402: "FaxProfile",
	403: "CodingMethods",
	404: "VersionYear",
	405: "ModeNumber",
	433: "Decode",
	434: "DefaultImageColor",
	512: "JPEGProc",
	513: "JPEGInterchangeFormat",
	514: "JPEGInterchangeFormatLength",
	515: "JPEGRestartInterval",
	517: "JPEGLosslessPredictors",
	518: "JPEGPointTransforms",
	519: "JPEGQTables",
	520: "JPEGDCTables",
	521: "JPEGACTables",
	529: "YCbCrCoefficients",
	530: "YCbCrSubSampling",
	531: "YCbCrPositioning",
	532: "ReferenceBlackWhite",
	559: "StripRowCounts",
	700: "XMP",
	18246: "Image.Rating",
	18249: "Image.RatingPercent",
	32781: "ImageID",
	32932: "Wang Annotation",
	33421: "CFARepeatPatternDim",
	33422: "CFAPattern",
	33423: "BatteryLevel",
	33432: "Copyright",
	33434: "ExposureTime",
	33437: "FNumber",
	33445: "MD FileTag",
	33446: "MD ScalePixel",
	33447: "MD ColorTable",
	33448: "MD LabName",
	33449: "MD SampleInfo",
	33450: "MD PrepDate",
	33451: "MD PrepTime",
	33452: "MD FileUnits",
	33550: "ModelPixelScaleTag",
	33723: "IPTC/NAA",
	33918: "INGR Packet Data Tag",
	33919: "INGR Flag Registers",
	33920: "IrasB Transformation Matrix",
	33922: "ModelTiepointTag",
	34016: "Site",
	34017: "ColorSequence",
	34018: "IT8Header",
	34019: "RasterPadding",
	34020: "BitsPerRunLength",
	34021: "BitsPerExtendedRunLength",
	34022: "ColorTable",
	34023: "ImageColorIndicator",
	34024: "BackgroundColorIndicator",
	34025: "ImageColorValue",
	34026: "BackgroundColorValue",
	34027: "PixelIntensityRange",
	34028: "TransparencyIndicator",
	34029: "ColorCharacterization",
	34030: "HCUsage",
	34031: "TrapIndicator",
	34032: "CMYKEquivalent",
	34033: "Reserved",
	34034: "Reserved",
	34035: "Reserved",
	34264: "ModelTransformationTag",
	34377: "Photoshop",
	34665: "Exif IFD",
	34675: "InterColorProfile",
	34732: "ImageLayer",
	34735: "GeoKeyDirectoryTag",
	34736: "GeoDoubleParamsTag",
	34737: "GeoAsciiParamsTag",
	34850: "ExposureProgram",
	34852: "SpectralSensitivity",
	34853: "GPSInfo",
	34855: "ISOSpeedRatings",
	34856: "OECF",
	34857: "Interlace",
	34858: "TimeZoneOffset",
	34859: "SelfTimeMode",
	34864: "SensitivityType",
	34865: "StandardOutputSensitivity",
	34866: "RecommendedExposureIndex",
	34867: "ISOSpeed",
	34868: "ISOSpeedLatitudeyyy",
	34869: "ISOSpeedLatitudezzz",
	34908: "HylaFAX FaxRecvParams",
	34909: "HylaFAX FaxSubAddress",
	34910: "HylaFAX FaxRecvTime",
	36864: "ExifVersion",
	36867: "DateTimeOriginal",
	36868: "DateTimeDigitized",
	37121: "ComponentsConfiguration",
	37122: "CompressedBitsPerPixel",
	37377: "ShutterSpeedValue",
	37378: "ApertureValue",
	37379: "BrightnessValue",
	37380: "ExposureBiasValue",
	37381: "MaxApertureValue",
	37382: "SubjectDistance",
	37383: "MeteringMode",
	37384: "LightSource",
	37385: "Flash",
	37386: "FocalLength",
	37387: "FlashEnergy",
	37388: "SpatialFrequencyResponse",
	37389: "Noise",
	37390: "FocalPlaneXResolution",
	37391: "FocalPlaneYResolution",
	37392: "FocalPlaneResolutionUnit",
	37393: "ImageNumber",
	37394: "SecurityClassification",
	37395: "ImageHistory",
	37396: "SubjectLocation",
	37397: "ExposureIndex",
	37398: "TIFF/EPStandardID",
	37399: "SensingMethod",
	37500: "MakerNote",
	37510: "UserComment",
	37520: "SubsecTime",
	37521: "SubsecTimeOriginal",
	37522: "SubsecTimeDigitized",
	37724: "ImageSourceData",
	40091: "XPTitle",
	40092: "XPComment",
	40093: "XPAuthor",
	40094: "XPKeywords",
	40095: "XPSubject",
	40960: "FlashpixVersion",
	40961: "ColorSpace",
	40962: "PixelXDimension",
	40963: "PixelYDimension",
	40964: "RelatedSoundFile",
	40965: "Interoperability IFD",
	41483: "FlashEnergy",
	41484: "SpatialFrequencyResponse",
	41486: "FocalPlaneXResolution",
	41487: "FocalPlaneYResolution",
	41488: "FocalPlaneResolutionUnit",
	41492: "SubjectLocation",
	41493: "ExposureIndex",
	41495: "SensingMethod",
	41728: "FileSource",
	41729: "SceneType",
	41730: "CFAPattern",
	41985: "CustomRendered",
	41986: "ExposureMode",
	41987: "WhiteBalance",
	41988: "DigitalZoomRatio",
	41989: "FocalLengthIn35mmFilm",
	41990: "SceneCaptureType",
	41991: "GainControl",
	41992: "Contrast",
	41993: "Saturation",
	41994: "Sharpness",
	41995: "DeviceSettingDescription",
	41996: "SubjectDistanceRange",
	42016: "ImageUniqueID",
	42032: "CameraOwnerName",
	42033: "BodySerialNumber",
	42034: "LensSpecification",
	42035: "LensMake",
	42036: "LensModel",
	42037: "LensSerialNumber",
	42112: "GDAL_METADATA",
	42113: "GDAL_NODATA",
	48129: "PixelFormat",
	48130: "Transformation",
	48131: "Uncompressed",
	48132: "ImageType",
	48256: "ImageWidth",
	48257: "ImageHeight",
	48258: "WidthResolution",
	48259: "HeightResolution",
	48320: "ImageOffset",
	48321: "ImageByteCount",
	48322: "AlphaOffset",
	48323: "AlphaByteCount",
	48324: "ImageDataDiscard",
	48325: "AlphaDataDiscard",
	50215: "Oce Scanjob Description",
	50216: "Oce Application Selector",
	50217: "Oce Identification Number",
	50218: "Oce ImageLogic Characteristics",
	50341: "PrintImageMatching",
	50706: "DNGVersion",
	50707: "DNGBackwardVersion",
	50708: "UniqueCameraModel",
	50709: "LocalizedCameraModel",
	50710: "CFAPlaneColor",
	50711: "CFALayout",
	50712: "LinearizationTable",
	50713: "BlackLevelRepeatDim",
	50714: "BlackLevel",
	50715: "BlackLevelDeltaH",
	50716: "BlackLevelDeltaV",
	50717: "WhiteLevel",
	50718: "DefaultScale",
	50719: "DefaultCropOrigin",
	50720: "DefaultCropSize",
	50721: "ColorMatrix1",
	50722: "ColorMatrix2",
	50723: "CameraCalibration1",
	50724: "CameraCalibration2",
	50725: "ReductionMatrix1",
	50726: "ReductionMatrix2",
	50727: "AnalogBalance",
	50728: "AsShotNeutral",
	50729: "AsShotWhiteXY",
	50730: "BaselineExposure",
	50731: "BaselineNoise",
	50732: "BaselineSharpness",
	50733: "BayerGreenSplit",
	50734: "LinearResponseLimit",
	50735: "CameraSerialNumber",
	50736: "LensInfo",
	50737: "ChromaBlurRadius",
	50738: "AntiAliasStrength",
	50739: "ShadowScale",
	50740: "DNGPrivateData",
	50741: "MakerNoteSafety",
	50778: "CalibrationIlluminant1",
	50779: "CalibrationIlluminant2",
	50780: "BestQualityScale",
	50781: "RawDataUniqueID",
	50784: "Alias Layer Metadata",
	50827: "OriginalRawFileName",
	50828: "OriginalRawFileData",
	50829: "ActiveArea",
	50830: "MaskedAreas",
	50831: "AsShotICCProfile",
	50832: "AsShotPreProfileMatrix",
	50833: "CurrentICCProfile",
	50834: "CurrentPreProfileMatrix",
	50879: "ColorimetricReference",
	50931: "CameraCalibrationSignature",
	50932: "ProfileCalibrationSignature",
	50933: "ExtraCameraProfiles",
	50934: "AsShotProfileName",
	50935: "NoiseReductionApplied",
	50936: "ProfileName",
	50937: "ProfileHueSatMapDims",
	50938: "ProfileHueSatMapData1",
	50939: "ProfileHueSatMapData2",
	50940: "ProfileToneCurve",
	50941: "ProfileEmbedPolicy",
	50942: "ProfileCopyright",
	50964: "ForwardMatrix1",
	50965: "ForwardMatrix2",
	50966: "PreviewApplicationName",
	50967: "PreviewApplicationVersion",
	50968: "PreviewSettingsName",
	50969: "PreviewSettingsDigest",
	50970: "PreviewColorSpace",
	50971: "PreviewDateTime",
	50972: "RawImageDigest",
	50973: "OriginalRawFileDigest",
	50974: "SubTileBlockSize",
	50975: "RowInterleaveFactor",
	50981: "ProfileLookTableDims",
	50982: "ProfileLookTableData",
	51008: "OpcodeList1",
	51009: "OpcodeList2",
	51022: "OpcodeList3",
	51041: "NoiseProfile",
	51089: "OriginalDefaultFinalSize",
	51090: "OriginalBestQualityFinalSize",
	51091: "OriginalDefaultCropSize",
	51107: "ProfileHueSatMapEncoding",
	51108: "ProfileLookTableEncoding",
	51109: "BaselineExposureOffset",
	51110: "DefaultBlackRender",
	51111: "NewRawImageDigest",
	51112: "RawToPreviewGain",
	51125: "DefaultUserCrop",
}