{*******************************************************}
{                                                       }
{       nrCommLib Communication Library for Delphi      }
{       UTF8 encoding/decoding (VCL)                    }
{       Copyright (c) 1997-2011 Roman Novgorodov        }
{       v9.27                                           }
{       last update: 1  Jan 2010                        }
{                                                       }
{       e-mail:noro@deepsoftware.com                    }
{       DeepSoftware                                    }
{       http://www.deepsoftware.com/                    }
{                                                       }
{*******************************************************}
{$I nrcommlib.inc}
unit nrutf8;
interface
uses windows;
type

   TnrDecoder = class
   public
     procedure Reset; virtual; abstract;
     function Decode(input : byte; var output : WideChar):boolean; virtual; abstract;
   end;

   TnrDecoderUTF8 = class(TnrDecoder)
   private
     lenExpected:integer;
     lenCurrent:integer;
     charCurrent: dword;
   public
     procedure Reset; override;
     function Decode(input : byte; var output : WideChar):boolean; override;
   end;

implementation

{ TnrDecoderUTF8 }

procedure TnrDecoderUTF8.Reset;
begin
  lenExpected  := 0;
  lenCurrent := 0;
  charCurrent := 0;
end;

function TnrDecoderUTF8.Decode(input: byte; var output: WideChar): boolean;
var mask, maskData : byte;
begin
  Result := False;

  // single ASCII char 0xxxxxxx
  if (input and $80) = 0 then begin
    Result := True;
    charCurrent := input;
  end else
  // leading byte 110xxxxx - 	1111110x
  if (input and $C0) = $C0 then begin
    mask := $20;
    maskData := $1F;
    lenExpected := 2;
    while ((mask and input) <> 0) and (mask <> 0) do begin
      mask := mask shr 1;
      maskData := maskData shr 1;
      Inc(lenExpected);
    end;
    charCurrent := input and maskData;
    lenCurrent := 1;
  end else
  // other data bytes 10xxxxxx
  if ((input and $C0) = $80) and (lenCurrent < lenExpected) then begin
    charCurrent := (charCurrent shl 6) or (input and $3F);
    Inc(lenCurrent);
    Result := lenCurrent >= lenExpected;
  end;

  if Result then begin
    output := WideChar(charCurrent);
    Reset;
  end;

end;

end.
