Skip to content

Commit 3de0d6a

Browse files
committed
pdf417: Avoid O(n^2) sequence copying in text-mode encoder
1 parent a858778 commit 3de0d6a

1 file changed

Lines changed: 58 additions & 55 deletions

File tree

src/pdf417.ps.src

Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -775,32 +775,47 @@ begin
775775
/in exch def
776776

777777
%
778-
% Each char adds 1 codeword, latches add up to 2
778+
% Codeword sequences are held as persistent chains of segments.
779+
% Each chain head is null or a node [parent_node, segment_array];
780+
% the final sequence is materialised once by walking the chain.
779781
%
780-
/maxseqlen in length 3 mul 4 add def
781-
782-
/seq0 [ 4 { maxseqlen array } repeat ] def
783-
/seq1 [ 4 { maxseqlen array } repeat ] def
784-
/len0 4 array def len0 0 0 put len0 1 0 put len0 2 0 put len0 3 0 put
785-
/len1 4 array def
786-
787-
/curseq seq0 def /curseqlen len0 def
788-
/nxtseq seq1 def /nxtseqlen len1 def
782+
/curseqhd [ //pdf417.textsubmodes length { null } repeat ] def
783+
/nxtseqhd [ //pdf417.textsubmodes length { null } repeat ] def
784+
/curseqlen [ //pdf417.textsubmodes length { 0 } repeat ] def
785+
/nxtseqlen [ //pdf417.textsubmodes length { 0 } repeat ] def
789786

790787
/curlen [ //pdf417.e //pdf417.e //pdf417.e //pdf417.e ] def
791788
curlen submode 0 put
792789

793790
%
794-
% Derive the optimal sequences ending in each submode
791+
% Flatten a chain of [parent, segment] cons cells of total length L
792+
% into a fresh array. The chain's deepest node holds the prefix, so
793+
% we walk head-to-root and write each segment into the tail of the
794+
% output, advancing pos backwards.
795795
%
796+
/flatten { % hd len -> array
797+
798+
dup array 3 1 roll % pos = len; out = array(len)
799+
800+
{ % out hd pos
801+
1 index null eq { exit } if % done when hd is null
802+
1 index 1 get % ... seg = hd[1]
803+
exch 1 index length sub % ... pos -= length(seg)
804+
3 index 1 index 3 index putinterval % out[pos..] = seg
805+
exch pop % drop seg
806+
exch 0 get exch % hd = hd[0]
807+
} loop
808+
809+
pop pop % return out
810+
811+
} def
812+
796813
in {
797814

798815
/char exch def
799816

800-
%
801-
% Check for optimisations in the current sequences by latching from x to y
802-
%
803-
{ % loop
817+
% Phase 1: relax curlen[*] via the static latch graph
818+
{
804819
/imp false def
805820
//pdf417.textsubmodes {
806821
/x exch def
@@ -809,67 +824,52 @@ begin
809824
/cost curlen x get //pdf417.latlen x get y get add def
810825
cost curlen y get lt {
811826
curlen y cost put
812-
% Copy curseq[x] to curseq[y] and append latseq codes
813-
/srclen curseqlen x get def
814-
srclen 0 gt {
815-
curseq y get 0 curseq x get 0 srclen getinterval putinterval
816-
} if
817-
//pdf417.latseq x get y get {
818-
curseq y get srclen 3 -1 roll put
819-
/srclen srclen 1 add def
820-
} forall
821-
curseqlen y srclen put
827+
/latyseg //pdf417.latseq x get y get def
828+
curseqhd y
829+
latyseg length 0 eq
830+
{ curseqhd x get }
831+
{ [ curseqhd x get latyseg ] }
832+
ifelse
833+
put
834+
curseqlen y curseqlen x get latyseg length add put
822835
/imp true def
823836
} if
824837
} forall
825838
} forall
826-
imp not {exit} if % Repeat unless no improvement
839+
imp not {exit} if
827840
} loop
828841

829-
%
830-
% Determine optimal next sequences for each valid encoding
831-
%
832842
/nxtlen [ //pdf417.e //pdf417.e //pdf417.e //pdf417.e ] def
833-
nxtseqlen 0 0 put nxtseqlen 1 0 put nxtseqlen 2 0 put nxtseqlen 3 0 put
843+
//pdf417.textsubmodes { nxtseqhd exch null put } forall
844+
//pdf417.textsubmodes { nxtseqlen exch 0 put } forall
834845

835846
//pdf417.textsubmodes {
836847
/x exch def
837848

838-
{ % loop for common exit
849+
{ % loop for common exit
839850

840851
charvals x get char known not {exit} if
841852

842-
%
843-
% Extend directly: copy curseq[x] + char to nxtseq[x]
844-
%
853+
% Phase 2: nxtseq[x] := curseq[x] + [char]
845854
/cost curlen x get 1 add def
846855
cost nxtlen x get lt {
847856
nxtlen x cost put
848-
/srclen curseqlen x get def
849-
srclen 0 gt {
850-
nxtseq x get 0 curseq x get 0 srclen getinterval putinterval
851-
} if
852-
nxtseq x get srclen char put
853-
nxtseqlen x srclen 1 add put
857+
nxtseqhd x [ curseqhd x get [ char ] ] put
858+
nxtseqlen x curseqlen x get 1 add put
854859
} if
855860

856-
%
857-
% Optimise for direct shifts from y to x
858-
%
861+
% Phase 3: shift y -> x for one char (y != x; stays in y)
859862
//pdf417.textsubmodes {
860863
/y exch def
861864
x y ne {
862865
/cost curlen y get //pdf417.shftlen y get x get add 1 add def
863866
cost nxtlen y get lt {
864867
nxtlen y cost put
865-
% Copy curseq[y] + shift + char to nxtseq[y]
866-
/srclen curseqlen y get def
867-
srclen 0 gt {
868-
nxtseq y get 0 curseq y get 0 srclen getinterval putinterval
869-
} if
870-
nxtseq y get srclen x //pdf417.a eq {//pdf417.as} {//pdf417.ps} ifelse put
871-
nxtseq y get srclen 1 add char put
872-
nxtseqlen y srclen 2 add put
868+
nxtseqhd y [
869+
curseqhd y get
870+
[ x //pdf417.a eq {//pdf417.as} {//pdf417.ps} ifelse char ]
871+
] put
872+
nxtseqlen y curseqlen y get 2 add put
873873
} if
874874
} if
875875
} forall
@@ -880,23 +880,26 @@ begin
880880
} forall
881881

882882
/curlen nxtlen def
883-
% Swap cur and nxt buffers
884-
curseq nxtseq /curseq exch def /nxtseq exch def
885-
curseqlen nxtseqlen /curseqlen exch def /nxtseqlen exch def
883+
curseqhd nxtseqhd /curseqhd exch def /nxtseqhd exch def
884+
curseqlen nxtseqlen /curseqlen exch def /nxtseqlen exch def
886885

887886
} forall
888887

889888
%
890889
% Select the optimal sequence
891890
%
892891
/minseq //pdf417.e def
892+
/bestsm -1 def
893893
//pdf417.textsubmodes {
894894
/k exch def
895895
curlen k get minseq lt {
896896
/minseq curlen k get def
897-
/txtseq curseq k get 0 curseqlen k get getinterval def
897+
/bestsm k def
898898
} if
899899
} forall
900+
bestsm -1 ne {
901+
/txtseq curseqhd bestsm get curseqlen bestsm get flatten def
902+
} if
900903

901904
%
902905
% Encode the sequence

0 commit comments

Comments
 (0)