Вопрос

My installer needs to change one line in a rather large (500Kb, about 11k lines) json file. I'm using AdvReplaceInFile to find and replace the occurrence.

Problem is, it takes about 3~ seconds to finish the job. I can kill the application using the same file, but if it's restarted automatically within that time window, even though the replace is successful, the process will overwrite the file again later on.

I'm assuming the algorithm responsible for search & replace is what's causing the slowdown.

How can I make it go faster other than writing my own DLL plugin to do so?

Это было полезно?

Решение

NSIS code was never designed to be fast and any call to a Int* function involves at least 2 string to number conversions etc.

Using a slightly slower version of the function you can prevent other processes from opening the file for writing:

Function AdvReplaceInFile
Exch $0 ;file to replace in
Exch
Exch $1 ;number to replace after
Exch
Exch 2
Exch $2 ;replace and onwards
Exch 2
Exch 3
Exch $3 ;replace with
Exch 3
Exch 4
Exch $4 ;to replace
Exch 4
Push $5 ;minus count
Push $6 ;universal
Push $7 ;end string
Push $8 ;left string
Push $9 ;right string
Push $R0 ;file1
Push $R1 ;file2
Push $R2 ;read
Push $R3 ;universal
Push $R4 ;count (onwards)
Push $R5 ;count (after)
Push $R6 ;temp file name

  GetTempFileName $R6
  FileOpen $R1 $0 a ;file to search in
  FileOpen $R0 $R6 a ;temp file
   StrLen $R3 $4
   StrCpy $R4 -1
   StrCpy $R5 -1

loop_read:
 ClearErrors
 FileRead $R1 $R2 ;read line
 IfErrors exit

   StrCpy $5 0
   StrCpy $7 $R2

loop_filter:
   IntOp $5 $5 - 1
   StrCpy $6 $7 $R3 $5 ;search
   StrCmp $6 "" file_write1
   StrCmp $6 $4 0 loop_filter

StrCpy $8 $7 $5 ;left part
IntOp $6 $5 + $R3
IntCmp $6 0 is0 not0
is0:
StrCpy $9 ""
Goto done
not0:
StrCpy $9 $7 "" $6 ;right part
done:
StrCpy $7 $8$3$9 ;re-join

IntOp $R4 $R4 + 1
StrCmp $2 all loop_filter
StrCmp $R4 $2 0 file_write2
IntOp $R4 $R4 - 1

IntOp $R5 $R5 + 1
StrCmp $1 all loop_filter
StrCmp $R5 $1 0 file_write1
IntOp $R5 $R5 - 1
Goto file_write2

file_write1:
 FileWrite $R0 $7 ;write modified line
Goto loop_read

file_write2:
 FileWrite $R0 $R2 ;write unmodified line
Goto loop_read

exit:
  FileSeek $R1 0 SET
  !if "${NSIS_PTR_SIZE}" > 4
  System::Call 'kernel32::SetEndOfFile(p$R1)'
  !else
  System::Call 'kernel32::SetEndOfFile(i$R1)'
  !endif
  FileSeek $R0 0 SET
  ClearErrors
  mov_loop:
    FileRead $R0 $R2
    IfErrors mov_done
    FileWrite $R1 $R2
    Goto mov_loop
  mov_done:
  FileClose $R0
  FileClose $R1
  SetDetailsPrint none
  Delete $R6
  SetDetailsPrint lastused

Pop $R6
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Pop $R0
Pop $9
Pop $8
Pop $7
Pop $6
Pop $5
;These values are stored in the stack in the reverse order they were pushed
Pop $0
Pop $1
Pop $2
Pop $3
Pop $4
FunctionEnd

The only way to actually make it faster would be to call a external program or write a plugin...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top