mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	
		
			
				
	
	
		
			252 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package mssql
 | |
| 
 | |
| import (
 | |
| 	"encoding/binary"
 | |
| 	"errors"
 | |
| 	"io"
 | |
| )
 | |
| 
 | |
| type packetType uint8
 | |
| 
 | |
| type header struct {
 | |
| 	PacketType packetType
 | |
| 	Status     uint8
 | |
| 	Size       uint16
 | |
| 	Spid       uint16
 | |
| 	PacketNo   uint8
 | |
| 	Pad        uint8
 | |
| }
 | |
| 
 | |
| // tdsBuffer reads and writes TDS packets of data to the transport.
 | |
| // The write and read buffers are separate to make sending attn signals
 | |
| // possible without locks. Currently attn signals are only sent during
 | |
| // reads, not writes.
 | |
| type tdsBuffer struct {
 | |
| 	transport io.ReadWriteCloser
 | |
| 
 | |
| 	packetSize int
 | |
| 
 | |
| 	// Write fields.
 | |
| 	wbuf        []byte
 | |
| 	wpos        int
 | |
| 	wPacketSeq  byte
 | |
| 	wPacketType packetType
 | |
| 
 | |
| 	// Read fields.
 | |
| 	rbuf        []byte
 | |
| 	rpos        int
 | |
| 	rsize       int
 | |
| 	final       bool
 | |
| 	rPacketType packetType
 | |
| 
 | |
| 	// afterFirst is assigned to right after tdsBuffer is created and
 | |
| 	// before the first use. It is executed after the first packet is
 | |
| 	// written and then removed.
 | |
| 	afterFirst func()
 | |
| }
 | |
| 
 | |
| func newTdsBuffer(bufsize uint16, transport io.ReadWriteCloser) *tdsBuffer {
 | |
| 	return &tdsBuffer{
 | |
| 		packetSize: int(bufsize),
 | |
| 		wbuf:       make([]byte, 1<<16),
 | |
| 		rbuf:       make([]byte, 1<<16),
 | |
| 		rpos:       8,
 | |
| 		transport:  transport,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (rw *tdsBuffer) ResizeBuffer(packetSize int) {
 | |
| 	rw.packetSize = packetSize
 | |
| }
 | |
| 
 | |
| func (w *tdsBuffer) PackageSize() int {
 | |
| 	return w.packetSize
 | |
| }
 | |
| 
 | |
| func (w *tdsBuffer) flush() (err error) {
 | |
| 	// Write packet size.
 | |
| 	w.wbuf[0] = byte(w.wPacketType)
 | |
| 	binary.BigEndian.PutUint16(w.wbuf[2:], uint16(w.wpos))
 | |
| 	w.wbuf[6] = w.wPacketSeq
 | |
| 
 | |
| 	// Write packet into underlying transport.
 | |
| 	if _, err = w.transport.Write(w.wbuf[:w.wpos]); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	// It is possible to create a whole new buffer after a flush.
 | |
| 	// Useful for debugging. Normally reuse the buffer.
 | |
| 	// w.wbuf = make([]byte, 1<<16)
 | |
| 
 | |
| 	// Execute afterFirst hook if it is set.
 | |
| 	if w.afterFirst != nil {
 | |
| 		w.afterFirst()
 | |
| 		w.afterFirst = nil
 | |
| 	}
 | |
| 
 | |
| 	w.wpos = 8
 | |
| 	w.wPacketSeq++
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *tdsBuffer) Write(p []byte) (total int, err error) {
 | |
| 	for {
 | |
| 		copied := copy(w.wbuf[w.wpos:w.packetSize], p)
 | |
| 		w.wpos += copied
 | |
| 		total += copied
 | |
| 		if copied == len(p) {
 | |
| 			return
 | |
| 		}
 | |
| 		if err = w.flush(); err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 		p = p[copied:]
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (w *tdsBuffer) WriteByte(b byte) error {
 | |
| 	if int(w.wpos) == len(w.wbuf) {
 | |
| 		if err := w.flush(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	w.wbuf[w.wpos] = b
 | |
| 	w.wpos += 1
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *tdsBuffer) BeginPacket(packetType packetType) {
 | |
| 	w.wbuf[1] = 0 // Packet is incomplete. This byte is set again in FinishPacket.
 | |
| 	w.wpos = 8
 | |
| 	w.wPacketSeq = 1
 | |
| 	w.wPacketType = packetType
 | |
| }
 | |
| 
 | |
| func (w *tdsBuffer) FinishPacket() error {
 | |
| 	w.wbuf[1] = 1 // Mark this as the last packet in the message.
 | |
| 	return w.flush()
 | |
| }
 | |
| 
 | |
| var headerSize = binary.Size(header{})
 | |
| 
 | |
| func (r *tdsBuffer) readNextPacket() error {
 | |
| 	h := header{}
 | |
| 	var err error
 | |
| 	err = binary.Read(r.transport, binary.BigEndian, &h)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if int(h.Size) > len(r.rbuf) {
 | |
| 		return errors.New("Invalid packet size, it is longer than buffer size")
 | |
| 	}
 | |
| 	if headerSize > int(h.Size) {
 | |
| 		return errors.New("Invalid packet size, it is shorter than header size")
 | |
| 	}
 | |
| 	_, err = io.ReadFull(r.transport, r.rbuf[headerSize:h.Size])
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	r.rpos = headerSize
 | |
| 	r.rsize = int(h.Size)
 | |
| 	r.final = h.Status != 0
 | |
| 	r.rPacketType = h.PacketType
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) BeginRead() (packetType, error) {
 | |
| 	err := r.readNextPacket()
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	return r.rPacketType, nil
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) ReadByte() (res byte, err error) {
 | |
| 	if r.rpos == r.rsize {
 | |
| 		if r.final {
 | |
| 			return 0, io.EOF
 | |
| 		}
 | |
| 		err = r.readNextPacket()
 | |
| 		if err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 	}
 | |
| 	res = r.rbuf[r.rpos]
 | |
| 	r.rpos++
 | |
| 	return res, nil
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) byte() byte {
 | |
| 	b, err := r.ReadByte()
 | |
| 	if err != nil {
 | |
| 		badStreamPanic(err)
 | |
| 	}
 | |
| 	return b
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) ReadFull(buf []byte) {
 | |
| 	_, err := io.ReadFull(r, buf[:])
 | |
| 	if err != nil {
 | |
| 		badStreamPanic(err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) uint64() uint64 {
 | |
| 	var buf [8]byte
 | |
| 	r.ReadFull(buf[:])
 | |
| 	return binary.LittleEndian.Uint64(buf[:])
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) int32() int32 {
 | |
| 	return int32(r.uint32())
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) uint32() uint32 {
 | |
| 	var buf [4]byte
 | |
| 	r.ReadFull(buf[:])
 | |
| 	return binary.LittleEndian.Uint32(buf[:])
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) uint16() uint16 {
 | |
| 	var buf [2]byte
 | |
| 	r.ReadFull(buf[:])
 | |
| 	return binary.LittleEndian.Uint16(buf[:])
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) BVarChar() string {
 | |
| 	l := int(r.byte())
 | |
| 	return r.readUcs2(l)
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) UsVarChar() string {
 | |
| 	l := int(r.uint16())
 | |
| 	return r.readUcs2(l)
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) readUcs2(numchars int) string {
 | |
| 	b := make([]byte, numchars*2)
 | |
| 	r.ReadFull(b)
 | |
| 	res, err := ucs22str(b)
 | |
| 	if err != nil {
 | |
| 		badStreamPanic(err)
 | |
| 	}
 | |
| 	return res
 | |
| }
 | |
| 
 | |
| func (r *tdsBuffer) Read(buf []byte) (copied int, err error) {
 | |
| 	copied = 0
 | |
| 	err = nil
 | |
| 	if r.rpos == r.rsize {
 | |
| 		if r.final {
 | |
| 			return 0, io.EOF
 | |
| 		}
 | |
| 		err = r.readNextPacket()
 | |
| 		if err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	copied = copy(buf, r.rbuf[r.rpos:r.rsize])
 | |
| 	r.rpos += copied
 | |
| 	return
 | |
| }
 |